4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
383 * @class Roo.bootstrap.Body
384 * @extends Roo.bootstrap.Component
385 * Bootstrap Body class
389 * @param {Object} config The config object
392 Roo.bootstrap.Body = function(config){
394 config = config || {};
396 Roo.bootstrap.Body.superclass.constructor.call(this, config);
397 this.el = Roo.get(config.el ? config.el : document.body );
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
405 is_body : true,// just to make sure it's constructed?
410 onRender : function(ct, position)
412 /* Roo.log("Roo.bootstrap.Body - onRender");
413 if (this.cls && this.cls.length) {
414 Roo.get(document.body).addClass(this.cls);
433 * @class Roo.bootstrap.ButtonGroup
434 * @extends Roo.bootstrap.Component
435 * Bootstrap ButtonGroup class
436 * @cfg {String} size lg | sm | xs (default empty normal)
437 * @cfg {String} align vertical | justified (default none)
438 * @cfg {String} direction up | down (default down)
439 * @cfg {Boolean} toolbar false | true
440 * @cfg {Boolean} btn true | false
445 * @param {Object} config The config object
448 Roo.bootstrap.ButtonGroup = function(config){
449 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
460 getAutoCreate : function(){
466 cfg.html = this.html || cfg.html;
477 if (['vertical','justified'].indexOf(this.align)!==-1) {
478 cfg.cls = 'btn-group-' + this.align;
480 if (this.align == 'justified') {
481 console.log(this.items);
485 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486 cfg.cls += ' btn-group-' + this.size;
489 if (this.direction == 'up') {
490 cfg.cls += ' dropup' ;
506 * @class Roo.bootstrap.Button
507 * @extends Roo.bootstrap.Component
508 * Bootstrap Button class
509 * @cfg {String} html The button content
510 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
511 * @cfg {String} size ( lg | sm | xs)
512 * @cfg {String} tag ( a | input | submit)
513 * @cfg {String} href empty or href
514 * @cfg {Boolean} disabled default false;
515 * @cfg {Boolean} isClose default false;
516 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
517 * @cfg {String} badge text for badge
518 * @cfg {String} theme default
519 * @cfg {Boolean} inverse
520 * @cfg {Boolean} toggle
521 * @cfg {String} ontext text for on toggle state
522 * @cfg {String} offtext text for off toggle state
523 * @cfg {Boolean} defaulton
524 * @cfg {Boolean} preventDefault default true
525 * @cfg {Boolean} removeClass remove the standard class..
526 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
529 * Create a new button
530 * @param {Object} config The config object
534 Roo.bootstrap.Button = function(config){
535 Roo.bootstrap.Button.superclass.constructor.call(this, config);
536 this.weightClass = ["btn-default",
548 * When a butotn is pressed
549 * @param {Roo.bootstrap.Button} this
550 * @param {Roo.EventObject} e
555 * After the button has been toggles
556 * @param {Roo.EventObject} e
557 * @param {boolean} pressed (also available as button.pressed)
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
581 preventDefault: true,
590 getAutoCreate : function(){
598 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
604 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
606 if (this.toggle == true) {
609 cls: 'slider-frame roo-button',
614 'data-off-text':'OFF',
615 cls: 'slider-button',
621 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622 cfg.cls += ' '+this.weight;
631 cfg["aria-hidden"] = true;
633 cfg.html = "×";
639 if (this.theme==='default') {
640 cfg.cls = 'btn roo-button';
642 //if (this.parentType != 'Navbar') {
643 this.weight = this.weight.length ? this.weight : 'default';
645 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647 cfg.cls += ' btn-' + this.weight;
649 } else if (this.theme==='glow') {
652 cfg.cls = 'btn-glow roo-button';
654 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
656 cfg.cls += ' ' + this.weight;
662 this.cls += ' inverse';
667 cfg.cls += ' active';
671 cfg.disabled = 'disabled';
675 Roo.log('changing to ul' );
677 this.glyphicon = 'caret';
680 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
682 //gsRoo.log(this.parentType);
683 if (this.parentType === 'Navbar' && !this.parent().bar) {
684 Roo.log('changing to li?');
693 href : this.href || '#'
696 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
697 cfg.cls += ' dropdown';
704 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
706 if (this.glyphicon) {
707 cfg.html = ' ' + cfg.html;
712 cls: 'glyphicon glyphicon-' + this.glyphicon
722 // cfg.cls='btn roo-button';
726 var value = cfg.html;
731 cls: 'glyphicon glyphicon-' + this.glyphicon,
750 cfg.cls += ' dropdown';
751 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
754 if (cfg.tag !== 'a' && this.href !== '') {
755 throw "Tag must be a to set href.";
756 } else if (this.href.length > 0) {
757 cfg.href = this.href;
760 if(this.removeClass){
765 cfg.target = this.target;
770 initEvents: function() {
771 // Roo.log('init events?');
772 // Roo.log(this.el.dom);
775 if (typeof (this.menu) != 'undefined') {
776 this.menu.parentType = this.xtype;
777 this.menu.triggerEl = this.el;
778 this.addxtype(Roo.apply({}, this.menu));
782 if (this.el.hasClass('roo-button')) {
783 this.el.on('click', this.onClick, this);
785 this.el.select('.roo-button').on('click', this.onClick, this);
788 if(this.removeClass){
789 this.el.on('click', this.onClick, this);
792 this.el.enableDisplayMode();
795 onClick : function(e)
802 Roo.log('button on click ');
803 if(this.preventDefault){
806 if (this.pressed === true || this.pressed === false) {
807 this.pressed = !this.pressed;
808 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809 this.fireEvent('toggle', this, e, this.pressed);
813 this.fireEvent('click', this, e);
817 * Enables this button
821 this.disabled = false;
822 this.el.removeClass('disabled');
826 * Disable this button
830 this.disabled = true;
831 this.el.addClass('disabled');
834 * sets the active state on/off,
835 * @param {Boolean} state (optional) Force a particular state
837 setActive : function(v) {
839 this.el[v ? 'addClass' : 'removeClass']('active');
842 * toggles the current active state
844 toggleActive : function()
846 var active = this.el.hasClass('active');
847 this.setActive(!active);
851 setText : function(str)
853 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
857 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
868 setWeight : function(str)
870 this.el.removeClass(this.weightClass);
871 this.el.addClass('btn-' + str);
885 * @class Roo.bootstrap.Column
886 * @extends Roo.bootstrap.Component
887 * Bootstrap Column class
888 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
898 * @cfg {Boolean} hidden (true|false) hide the element
899 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900 * @cfg {String} fa (ban|check|...) font awesome icon
901 * @cfg {Number} fasize (1|2|....) font awsome size
903 * @cfg {String} icon (info-sign|check|...) glyphicon name
905 * @cfg {String} html content of column.
908 * Create a new Column
909 * @param {Object} config The config object
912 Roo.bootstrap.Column = function(config){
913 Roo.bootstrap.Column.superclass.constructor.call(this, config);
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
934 getAutoCreate : function(){
935 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
943 ['xs','sm','md','lg'].map(function(size){
944 //Roo.log( size + ':' + settings[size]);
946 if (settings[size+'off'] !== false) {
947 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
950 if (settings[size] === false) {
954 if (!settings[size]) { // 0 = hidden
955 cfg.cls += ' hidden-' + size;
958 cfg.cls += ' col-' + size + '-' + settings[size];
963 cfg.cls += ' hidden';
966 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967 cfg.cls +=' alert alert-' + this.alert;
971 if (this.html.length) {
972 cfg.html = this.html;
976 if (this.fasize > 1) {
977 fasize = ' fa-' + this.fasize + 'x';
979 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
984 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1003 * @class Roo.bootstrap.Container
1004 * @extends Roo.bootstrap.Component
1005 * Bootstrap Container class
1006 * @cfg {Boolean} jumbotron is it a jumbotron element
1007 * @cfg {String} html content of element
1008 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
1010 * @cfg {String} header content of header (for panel)
1011 * @cfg {String} footer content of footer (for panel)
1012 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013 * @cfg {String} tag (header|aside|section) type of HTML tag.
1014 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015 * @cfg {String} fa font awesome icon
1016 * @cfg {String} icon (info-sign|check|...) glyphicon name
1017 * @cfg {Boolean} hidden (true|false) hide the element
1018 * @cfg {Boolean} expandable (true|false) default false
1019 * @cfg {Boolean} expanded (true|false) default true
1020 * @cfg {String} rheader contet on the right of header
1021 * @cfg {Boolean} clickable (true|false) default false
1025 * Create a new Container
1026 * @param {Object} config The config object
1029 Roo.bootstrap.Container = function(config){
1030 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1036 * After the panel has been expand
1038 * @param {Roo.bootstrap.Container} this
1043 * After the panel has been collapsed
1045 * @param {Roo.bootstrap.Container} this
1050 * When a element is chick
1051 * @param {Roo.bootstrap.Container} this
1052 * @param {Roo.EventObject} e
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1076 getChildContainer : function() {
1082 if (this.panel.length) {
1083 return this.el.select('.panel-body',true).first();
1090 getAutoCreate : function(){
1093 tag : this.tag || 'div',
1097 if (this.jumbotron) {
1098 cfg.cls = 'jumbotron';
1103 // - this is applied by the parent..
1105 // cfg.cls = this.cls + '';
1108 if (this.sticky.length) {
1110 var bd = Roo.get(document.body);
1111 if (!bd.hasClass('bootstrap-sticky')) {
1112 bd.addClass('bootstrap-sticky');
1113 Roo.select('html',true).setStyle('height', '100%');
1116 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1120 if (this.well.length) {
1121 switch (this.well) {
1124 cfg.cls +=' well well-' +this.well;
1133 cfg.cls += ' hidden';
1137 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138 cfg.cls +=' alert alert-' + this.alert;
1143 if (this.panel.length) {
1144 cfg.cls += ' panel panel-' + this.panel;
1146 if (this.header.length) {
1150 if(this.expandable){
1152 cfg.cls = cfg.cls + ' expandable';
1156 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1164 cls : 'panel-title',
1165 html : (this.expandable ? ' ' : '') + this.header
1169 cls: 'panel-header-right',
1175 cls : 'panel-heading',
1176 style : this.expandable ? 'cursor: pointer' : '',
1184 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1189 if (this.footer.length) {
1191 cls : 'panel-footer',
1200 body.html = this.html || cfg.html;
1201 // prefix with the icons..
1203 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1206 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1211 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212 cfg.cls = 'container';
1218 initEvents: function()
1220 if(this.expandable){
1221 var headerEl = this.headerEl();
1224 headerEl.on('click', this.onToggleClick, this);
1229 this.el.on('click', this.onClick, this);
1234 onToggleClick : function()
1236 var headerEl = this.headerEl();
1252 if(this.fireEvent('expand', this)) {
1254 this.expanded = true;
1256 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1258 this.el.select('.panel-body',true).first().removeClass('hide');
1260 var toggleEl = this.toggleEl();
1266 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1271 collapse : function()
1273 if(this.fireEvent('collapse', this)) {
1275 this.expanded = false;
1277 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278 this.el.select('.panel-body',true).first().addClass('hide');
1280 var toggleEl = this.toggleEl();
1286 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1290 toggleEl : function()
1292 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1296 return this.el.select('.panel-heading .fa',true).first();
1299 headerEl : function()
1301 if(!this.el || !this.panel.length || !this.header.length){
1305 return this.el.select('.panel-heading',true).first()
1310 if(!this.el || !this.panel.length){
1314 return this.el.select('.panel-body',true).first()
1317 titleEl : function()
1319 if(!this.el || !this.panel.length || !this.header.length){
1323 return this.el.select('.panel-title',true).first();
1326 setTitle : function(v)
1328 var titleEl = this.titleEl();
1334 titleEl.dom.innerHTML = v;
1337 getTitle : function()
1340 var titleEl = this.titleEl();
1346 return titleEl.dom.innerHTML;
1349 setRightTitle : function(v)
1351 var t = this.el.select('.panel-header-right',true).first();
1357 t.dom.innerHTML = v;
1360 onClick : function(e)
1364 this.fireEvent('click', this, e);
1378 * @class Roo.bootstrap.Img
1379 * @extends Roo.bootstrap.Component
1380 * Bootstrap Img class
1381 * @cfg {Boolean} imgResponsive false | true
1382 * @cfg {String} border rounded | circle | thumbnail
1383 * @cfg {String} src image source
1384 * @cfg {String} alt image alternative text
1385 * @cfg {String} href a tag href
1386 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387 * @cfg {String} xsUrl xs image source
1388 * @cfg {String} smUrl sm image source
1389 * @cfg {String} mdUrl md image source
1390 * @cfg {String} lgUrl lg image source
1393 * Create a new Input
1394 * @param {Object} config The config object
1397 Roo.bootstrap.Img = function(config){
1398 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1404 * The img click event for the img.
1405 * @param {Roo.EventObject} e
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1413 imgResponsive: true,
1423 getAutoCreate : function()
1425 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426 return this.createSingleImg();
1431 cls: 'roo-image-responsive-group',
1436 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1438 if(!_this[size + 'Url']){
1444 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445 html: _this.html || cfg.html,
1446 src: _this[size + 'Url']
1449 img.cls += ' roo-image-responsive-' + size;
1451 var s = ['xs', 'sm', 'md', 'lg'];
1453 s.splice(s.indexOf(size), 1);
1455 Roo.each(s, function(ss){
1456 img.cls += ' hidden-' + ss;
1459 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460 cfg.cls += ' img-' + _this.border;
1464 cfg.alt = _this.alt;
1477 a.target = _this.target;
1481 cfg.cn.push((_this.href) ? a : img);
1488 createSingleImg : function()
1492 cls: (this.imgResponsive) ? 'img-responsive' : '',
1494 src : 'about:blank' // just incase src get's set to undefined?!?
1497 cfg.html = this.html || cfg.html;
1499 cfg.src = this.src || cfg.src;
1501 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502 cfg.cls += ' img-' + this.border;
1519 a.target = this.target;
1524 return (this.href) ? a : cfg;
1527 initEvents: function()
1530 this.el.on('click', this.onClick, this);
1535 onClick : function(e)
1537 Roo.log('img onclick');
1538 this.fireEvent('click', this, e);
1541 * Sets the url of the image - used to update it
1542 * @param {String} url the url of the image
1545 setSrc : function(url)
1549 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550 this.el.dom.src = url;
1554 this.el.select('img', true).first().dom.src = url;
1570 * @class Roo.bootstrap.Link
1571 * @extends Roo.bootstrap.Component
1572 * Bootstrap Link Class
1573 * @cfg {String} alt image alternative text
1574 * @cfg {String} href a tag href
1575 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576 * @cfg {String} html the content of the link.
1577 * @cfg {String} anchor name for the anchor link
1578 * @cfg {String} fa - favicon
1580 * @cfg {Boolean} preventDefault (true | false) default false
1584 * Create a new Input
1585 * @param {Object} config The config object
1588 Roo.bootstrap.Link = function(config){
1589 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1595 * The img click event for the img.
1596 * @param {Roo.EventObject} e
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1606 preventDefault: false,
1612 getAutoCreate : function()
1614 var html = this.html || '';
1616 if (this.fa !== false) {
1617 html = '<i class="fa fa-' + this.fa + '"></i>';
1622 // anchor's do not require html/href...
1623 if (this.anchor === false) {
1625 cfg.href = this.href || '#';
1627 cfg.name = this.anchor;
1628 if (this.html !== false || this.fa !== false) {
1631 if (this.href !== false) {
1632 cfg.href = this.href;
1636 if(this.alt !== false){
1641 if(this.target !== false) {
1642 cfg.target = this.target;
1648 initEvents: function() {
1650 if(!this.href || this.preventDefault){
1651 this.el.on('click', this.onClick, this);
1655 onClick : function(e)
1657 if(this.preventDefault){
1660 //Roo.log('img onclick');
1661 this.fireEvent('click', this, e);
1674 * @class Roo.bootstrap.Header
1675 * @extends Roo.bootstrap.Component
1676 * Bootstrap Header class
1677 * @cfg {String} html content of header
1678 * @cfg {Number} level (1|2|3|4|5|6) default 1
1681 * Create a new Header
1682 * @param {Object} config The config object
1686 Roo.bootstrap.Header = function(config){
1687 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1698 getAutoCreate : function(){
1703 tag: 'h' + (1 *this.level),
1704 html: this.html || ''
1716 * Ext JS Library 1.1.1
1717 * Copyright(c) 2006-2007, Ext JS, LLC.
1719 * Originally Released Under LGPL - original licence link has changed is not relivant.
1722 * <script type="text/javascript">
1726 * @class Roo.bootstrap.MenuMgr
1727 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1730 Roo.bootstrap.MenuMgr = function(){
1731 var menus, active, groups = {}, attached = false, lastShow = new Date();
1733 // private - called when first menu is created
1736 active = new Roo.util.MixedCollection();
1737 Roo.get(document).addKeyListener(27, function(){
1738 if(active.length > 0){
1746 if(active && active.length > 0){
1747 var c = active.clone();
1757 if(active.length < 1){
1758 Roo.get(document).un("mouseup", onMouseDown);
1766 var last = active.last();
1767 lastShow = new Date();
1770 Roo.get(document).on("mouseup", onMouseDown);
1775 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776 m.parentMenu.activeChild = m;
1777 }else if(last && last.isVisible()){
1778 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1783 function onBeforeHide(m){
1785 m.activeChild.hide();
1787 if(m.autoHideTimer){
1788 clearTimeout(m.autoHideTimer);
1789 delete m.autoHideTimer;
1794 function onBeforeShow(m){
1795 var pm = m.parentMenu;
1796 if(!pm && !m.allowOtherMenus){
1798 }else if(pm && pm.activeChild && active != m){
1799 pm.activeChild.hide();
1803 // private this should really trigger on mouseup..
1804 function onMouseDown(e){
1805 Roo.log("on Mouse Up");
1807 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808 Roo.log("MenuManager hideAll");
1817 function onBeforeCheck(mi, state){
1819 var g = groups[mi.group];
1820 for(var i = 0, l = g.length; i < l; i++){
1822 g[i].setChecked(false);
1831 * Hides all menus that are currently visible
1833 hideAll : function(){
1838 register : function(menu){
1842 menus[menu.id] = menu;
1843 menu.on("beforehide", onBeforeHide);
1844 menu.on("hide", onHide);
1845 menu.on("beforeshow", onBeforeShow);
1846 menu.on("show", onShow);
1848 if(g && menu.events["checkchange"]){
1852 groups[g].push(menu);
1853 menu.on("checkchange", onCheck);
1858 * Returns a {@link Roo.menu.Menu} object
1859 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860 * be used to generate and return a new Menu instance.
1862 get : function(menu){
1863 if(typeof menu == "string"){ // menu id
1865 }else if(menu.events){ // menu instance
1868 /*else if(typeof menu.length == 'number'){ // array of menu items?
1869 return new Roo.bootstrap.Menu({items:menu});
1870 }else{ // otherwise, must be a config
1871 return new Roo.bootstrap.Menu(menu);
1878 unregister : function(menu){
1879 delete menus[menu.id];
1880 menu.un("beforehide", onBeforeHide);
1881 menu.un("hide", onHide);
1882 menu.un("beforeshow", onBeforeShow);
1883 menu.un("show", onShow);
1885 if(g && menu.events["checkchange"]){
1886 groups[g].remove(menu);
1887 menu.un("checkchange", onCheck);
1892 registerCheckable : function(menuItem){
1893 var g = menuItem.group;
1898 groups[g].push(menuItem);
1899 menuItem.on("beforecheckchange", onBeforeCheck);
1904 unregisterCheckable : function(menuItem){
1905 var g = menuItem.group;
1907 groups[g].remove(menuItem);
1908 menuItem.un("beforecheckchange", onBeforeCheck);
1920 * @class Roo.bootstrap.Menu
1921 * @extends Roo.bootstrap.Component
1922 * Bootstrap Menu class - container for MenuItems
1923 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924 * @cfg {bool} hidden if the menu should be hidden when rendered.
1925 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1926 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1930 * @param {Object} config The config object
1934 Roo.bootstrap.Menu = function(config){
1935 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936 if (this.registerMenu && this.type != 'treeview') {
1937 Roo.bootstrap.MenuMgr.register(this);
1942 * Fires before this menu is displayed
1943 * @param {Roo.menu.Menu} this
1948 * Fires before this menu is hidden
1949 * @param {Roo.menu.Menu} this
1954 * Fires after this menu is displayed
1955 * @param {Roo.menu.Menu} this
1960 * Fires after this menu is hidden
1961 * @param {Roo.menu.Menu} this
1966 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967 * @param {Roo.menu.Menu} this
1968 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969 * @param {Roo.EventObject} e
1974 * Fires when the mouse is hovering over this menu
1975 * @param {Roo.menu.Menu} this
1976 * @param {Roo.EventObject} e
1977 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1982 * Fires when the mouse exits this menu
1983 * @param {Roo.menu.Menu} this
1984 * @param {Roo.EventObject} e
1985 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1990 * Fires when a menu item contained in this menu is clicked
1991 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992 * @param {Roo.EventObject} e
1996 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2003 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2006 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2008 registerMenu : true,
2010 menuItems :false, // stores the menu items..
2020 getChildContainer : function() {
2024 getAutoCreate : function(){
2026 //if (['right'].indexOf(this.align)!==-1) {
2027 // cfg.cn[1].cls += ' pull-right'
2033 cls : 'dropdown-menu' ,
2034 style : 'z-index:1000'
2038 if (this.type === 'submenu') {
2039 cfg.cls = 'submenu active';
2041 if (this.type === 'treeview') {
2042 cfg.cls = 'treeview-menu';
2047 initEvents : function() {
2049 // Roo.log("ADD event");
2050 // Roo.log(this.triggerEl.dom);
2052 this.triggerEl.on('click', this.onTriggerClick, this);
2054 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2056 this.triggerEl.addClass('dropdown-toggle');
2059 this.el.on('touchstart' , this.onTouch, this);
2061 this.el.on('click' , this.onClick, this);
2063 this.el.on("mouseover", this.onMouseOver, this);
2064 this.el.on("mouseout", this.onMouseOut, this);
2068 findTargetItem : function(e)
2070 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2074 //Roo.log(t); Roo.log(t.id);
2076 //Roo.log(this.menuitems);
2077 return this.menuitems.get(t.id);
2079 //return this.items.get(t.menuItemId);
2085 onTouch : function(e)
2087 Roo.log("menu.onTouch");
2088 //e.stopEvent(); this make the user popdown broken
2092 onClick : function(e)
2094 Roo.log("menu.onClick");
2096 var t = this.findTargetItem(e);
2097 if(!t || t.isContainer){
2102 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2103 if(t == this.activeItem && t.shouldDeactivate(e)){
2104 this.activeItem.deactivate();
2105 delete this.activeItem;
2109 this.setActiveItem(t, true);
2117 Roo.log('pass click event');
2121 this.fireEvent("click", this, t, e);
2125 if(!t.href.length || t.href == '#'){
2126 (function() { _this.hide(); }).defer(100);
2131 onMouseOver : function(e){
2132 var t = this.findTargetItem(e);
2135 // if(t.canActivate && !t.disabled){
2136 // this.setActiveItem(t, true);
2140 this.fireEvent("mouseover", this, e, t);
2142 isVisible : function(){
2143 return !this.hidden;
2145 onMouseOut : function(e){
2146 var t = this.findTargetItem(e);
2149 // if(t == this.activeItem && t.shouldDeactivate(e)){
2150 // this.activeItem.deactivate();
2151 // delete this.activeItem;
2154 this.fireEvent("mouseout", this, e, t);
2159 * Displays this menu relative to another element
2160 * @param {String/HTMLElement/Roo.Element} element The element to align to
2161 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162 * the element (defaults to this.defaultAlign)
2163 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2165 show : function(el, pos, parentMenu){
2166 this.parentMenu = parentMenu;
2170 this.fireEvent("beforeshow", this);
2171 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2174 * Displays this menu at a specific xy position
2175 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2178 showAt : function(xy, parentMenu, /* private: */_e){
2179 this.parentMenu = parentMenu;
2184 this.fireEvent("beforeshow", this);
2185 //xy = this.el.adjustForConstraints(xy);
2189 this.hideMenuItems();
2190 this.hidden = false;
2191 this.triggerEl.addClass('open');
2193 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2197 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2202 this.fireEvent("show", this);
2208 this.doFocus.defer(50, this);
2212 doFocus : function(){
2214 this.focusEl.focus();
2219 * Hides this menu and optionally all parent menus
2220 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2222 hide : function(deep)
2225 this.hideMenuItems();
2226 if(this.el && this.isVisible()){
2227 this.fireEvent("beforehide", this);
2228 if(this.activeItem){
2229 this.activeItem.deactivate();
2230 this.activeItem = null;
2232 this.triggerEl.removeClass('open');;
2234 this.fireEvent("hide", this);
2236 if(deep === true && this.parentMenu){
2237 this.parentMenu.hide(true);
2241 onTriggerClick : function(e)
2243 Roo.log('trigger click');
2245 var target = e.getTarget();
2247 Roo.log(target.nodeName.toLowerCase());
2249 if(target.nodeName.toLowerCase() === 'i'){
2255 onTriggerPress : function(e)
2257 Roo.log('trigger press');
2258 //Roo.log(e.getTarget());
2259 // Roo.log(this.triggerEl.dom);
2261 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262 var pel = Roo.get(e.getTarget());
2263 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264 Roo.log('is treeview or dropdown?');
2268 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2272 if (this.isVisible()) {
2277 this.show(this.triggerEl, false, false);
2280 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2287 hideMenuItems : function()
2289 Roo.log("hide Menu Items");
2293 //$(backdrop).remove()
2294 this.el.select('.open',true).each(function(aa) {
2296 aa.removeClass('open');
2297 //var parent = getParent($(this))
2298 //var relatedTarget = { relatedTarget: this }
2300 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301 //if (e.isDefaultPrevented()) return
2302 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2305 addxtypeChild : function (tree, cntr) {
2306 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2308 this.menuitems.add(comp);
2329 * @class Roo.bootstrap.MenuItem
2330 * @extends Roo.bootstrap.Component
2331 * Bootstrap MenuItem class
2332 * @cfg {String} html the menu label
2333 * @cfg {String} href the link
2334 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336 * @cfg {Boolean} active used on sidebars to highlight active itesm
2337 * @cfg {String} fa favicon to show on left of menu item.
2338 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2342 * Create a new MenuItem
2343 * @param {Object} config The config object
2347 Roo.bootstrap.MenuItem = function(config){
2348 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2353 * The raw click event for the entire grid.
2354 * @param {Roo.bootstrap.MenuItem} this
2355 * @param {Roo.EventObject} e
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2365 preventDefault: false,
2366 isContainer : false,
2370 getAutoCreate : function(){
2372 if(this.isContainer){
2375 cls: 'dropdown-menu-item'
2389 if (this.fa !== false) {
2392 cls : 'fa fa-' + this.fa
2401 cls: 'dropdown-menu-item',
2404 if (this.parent().type == 'treeview') {
2405 cfg.cls = 'treeview-menu';
2408 cfg.cls += ' active';
2413 anc.href = this.href || cfg.cn[0].href ;
2414 ctag.html = this.html || cfg.cn[0].html ;
2418 initEvents: function()
2420 if (this.parent().type == 'treeview') {
2421 this.el.select('a').on('click', this.onClick, this);
2425 this.menu.parentType = this.xtype;
2426 this.menu.triggerEl = this.el;
2427 this.menu = this.addxtype(Roo.apply({}, this.menu));
2431 onClick : function(e)
2433 Roo.log('item on click ');
2435 if(this.preventDefault){
2438 //this.parent().hideMenuItems();
2440 this.fireEvent('click', this, e);
2459 * @class Roo.bootstrap.MenuSeparator
2460 * @extends Roo.bootstrap.Component
2461 * Bootstrap MenuSeparator class
2464 * Create a new MenuItem
2465 * @param {Object} config The config object
2469 Roo.bootstrap.MenuSeparator = function(config){
2470 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2475 getAutoCreate : function(){
2494 * @class Roo.bootstrap.Modal
2495 * @extends Roo.bootstrap.Component
2496 * Bootstrap Modal class
2497 * @cfg {String} title Title of dialog
2498 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2500 * @cfg {Boolean} specificTitle default false
2501 * @cfg {Array} buttons Array of buttons or standard button set..
2502 * @cfg {String} buttonPosition (left|right|center) default right
2503 * @cfg {Boolean} animate default true
2504 * @cfg {Boolean} allow_close default true
2505 * @cfg {Boolean} fitwindow default false
2506 * @cfg {String} size (sm|lg) default empty
2510 * Create a new Modal Dialog
2511 * @param {Object} config The config object
2514 Roo.bootstrap.Modal = function(config){
2515 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2520 * The raw btnclick event for the button
2521 * @param {Roo.EventObject} e
2526 * Fire when dialog resize
2527 * @param {Roo.bootstrap.Modal} this
2528 * @param {Roo.EventObject} e
2532 this.buttons = this.buttons || [];
2535 this.tmpl = Roo.factory(this.tmpl);
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2542 title : 'test dialog',
2552 specificTitle: false,
2554 buttonPosition: 'right',
2573 onRender : function(ct, position)
2575 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2578 var cfg = Roo.apply({}, this.getAutoCreate());
2581 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2583 //if (!cfg.name.length) {
2587 cfg.cls += ' ' + this.cls;
2590 cfg.style = this.style;
2592 this.el = Roo.get(document.body).createChild(cfg, position);
2594 //var type = this.el.dom.type;
2597 if(this.tabIndex !== undefined){
2598 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2601 this.dialogEl = this.el.select('.modal-dialog',true).first();
2602 this.bodyEl = this.el.select('.modal-body',true).first();
2603 this.closeEl = this.el.select('.modal-header .close', true).first();
2604 this.headerEl = this.el.select('.modal-header',true).first();
2605 this.titleEl = this.el.select('.modal-title',true).first();
2606 this.footerEl = this.el.select('.modal-footer',true).first();
2608 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609 this.maskEl.enableDisplayMode("block");
2611 //this.el.addClass("x-dlg-modal");
2613 if (this.buttons.length) {
2614 Roo.each(this.buttons, function(bb) {
2615 var b = Roo.apply({}, bb);
2616 b.xns = b.xns || Roo.bootstrap;
2617 b.xtype = b.xtype || 'Button';
2618 if (typeof(b.listeners) == 'undefined') {
2619 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2622 var btn = Roo.factory(b);
2624 btn.render(this.el.select('.modal-footer div').first());
2628 // render the children.
2631 if(typeof(this.items) != 'undefined'){
2632 var items = this.items;
2635 for(var i =0;i < items.length;i++) {
2636 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2640 this.items = nitems;
2642 // where are these used - they used to be body/close/footer
2646 //this.el.addClass([this.fieldClass, this.cls]);
2650 getAutoCreate : function(){
2655 html : this.html || ''
2660 cls : 'modal-title',
2664 if(this.specificTitle){
2670 if (this.allow_close) {
2682 if(this.size.length){
2683 size = 'modal-' + this.size;
2688 style : 'display: none',
2691 cls: "modal-dialog " + size,
2694 cls : "modal-content",
2697 cls : 'modal-header',
2702 cls : 'modal-footer',
2706 cls: 'btn-' + this.buttonPosition
2723 modal.cls += ' fade';
2729 getChildContainer : function() {
2734 getButtonContainer : function() {
2735 return this.el.select('.modal-footer div',true).first();
2738 initEvents : function()
2740 if (this.allow_close) {
2741 this.closeEl.on('click', this.hide, this);
2743 Roo.EventManager.onWindowResize(this.resize, this, true);
2750 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2751 if (this.fitwindow) {
2752 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2758 setSize : function(w,h)
2768 if (!this.rendered) {
2772 this.el.setStyle('display', 'block');
2774 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2777 this.el.addClass('in');
2780 this.el.addClass('in');
2784 // not sure how we can show data in here..
2786 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2789 Roo.get(document.body).addClass("x-body-masked");
2791 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2792 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2797 this.fireEvent('show', this);
2799 // set zindex here - otherwise it appears to be ignored...
2800 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2803 this.items.forEach( function(e) {
2804 e.layout ? e.layout() : false;
2812 if(this.fireEvent("beforehide", this) !== false){
2814 Roo.get(document.body).removeClass("x-body-masked");
2815 this.el.removeClass('in');
2816 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2818 if(this.animate){ // why
2820 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2822 this.el.setStyle('display', 'none');
2824 this.fireEvent('hide', this);
2828 addButton : function(str, cb)
2832 var b = Roo.apply({}, { html : str } );
2833 b.xns = b.xns || Roo.bootstrap;
2834 b.xtype = b.xtype || 'Button';
2835 if (typeof(b.listeners) == 'undefined') {
2836 b.listeners = { click : cb.createDelegate(this) };
2839 var btn = Roo.factory(b);
2841 btn.render(this.el.select('.modal-footer div').first());
2847 setDefaultButton : function(btn)
2849 //this.el.select('.modal-footer').()
2853 resizeTo: function(w,h)
2857 this.dialogEl.setWidth(w);
2858 if (this.diff === false) {
2859 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2862 this.bodyEl.setHeight(h-this.diff);
2864 this.fireEvent('resize', this);
2867 setContentSize : function(w, h)
2871 onButtonClick: function(btn,e)
2874 this.fireEvent('btnclick', btn.name, e);
2877 * Set the title of the Dialog
2878 * @param {String} str new Title
2880 setTitle: function(str) {
2881 this.titleEl.dom.innerHTML = str;
2884 * Set the body of the Dialog
2885 * @param {String} str new Title
2887 setBody: function(str) {
2888 this.bodyEl.dom.innerHTML = str;
2891 * Set the body of the Dialog using the template
2892 * @param {Obj} data - apply this data to the template and replace the body contents.
2894 applyBody: function(obj)
2897 Roo.log("Error - using apply Body without a template");
2900 this.tmpl.overwrite(this.bodyEl, obj);
2906 Roo.apply(Roo.bootstrap.Modal, {
2908 * Button config that displays a single OK button
2917 * Button config that displays Yes and No buttons
2933 * Button config that displays OK and Cancel buttons
2948 * Button config that displays Yes, No and Cancel buttons
2972 * messagebox - can be used as a replace
2976 * @class Roo.MessageBox
2977 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2986 // process text value...
2990 // Show a dialog using config options:
2992 title:'Save Changes?',
2993 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994 buttons: Roo.Msg.YESNOCANCEL,
3001 Roo.bootstrap.MessageBox = function(){
3002 var dlg, opt, mask, waitTimer;
3003 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004 var buttons, activeTextEl, bwidth;
3008 var handleButton = function(button){
3010 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3014 var handleHide = function(){
3016 dlg.el.removeClass(opt.cls);
3019 // Roo.TaskMgr.stop(waitTimer);
3020 // waitTimer = null;
3025 var updateButtons = function(b){
3028 buttons["ok"].hide();
3029 buttons["cancel"].hide();
3030 buttons["yes"].hide();
3031 buttons["no"].hide();
3032 //dlg.footer.dom.style.display = 'none';
3035 dlg.footerEl.dom.style.display = '';
3036 for(var k in buttons){
3037 if(typeof buttons[k] != "function"){
3040 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041 width += buttons[k].el.getWidth()+15;
3051 var handleEsc = function(d, k, e){
3052 if(opt && opt.closable !== false){
3062 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063 * @return {Roo.BasicDialog} The BasicDialog element
3065 getDialog : function(){
3067 dlg = new Roo.bootstrap.Modal( {
3070 //constraintoviewport:false,
3072 //collapsible : false,
3077 //buttonAlign:"center",
3078 closeClick : function(){
3079 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3082 handleButton("cancel");
3087 dlg.on("hide", handleHide);
3089 //dlg.addKeyListener(27, handleEsc);
3091 this.buttons = buttons;
3092 var bt = this.buttonText;
3093 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3098 bodyEl = dlg.bodyEl.createChild({
3100 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101 '<textarea class="roo-mb-textarea"></textarea>' +
3102 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3104 msgEl = bodyEl.dom.firstChild;
3105 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106 textboxEl.enableDisplayMode();
3107 textboxEl.addKeyListener([10,13], function(){
3108 if(dlg.isVisible() && opt && opt.buttons){
3111 }else if(opt.buttons.yes){
3112 handleButton("yes");
3116 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117 textareaEl.enableDisplayMode();
3118 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119 progressEl.enableDisplayMode();
3121 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122 //var pf = progressEl.dom.firstChild;
3124 //pp = Roo.get(pf.firstChild);
3125 //pp.setHeight(pf.offsetHeight);
3133 * Updates the message box body text
3134 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135 * the XHTML-compliant non-breaking space character '&#160;')
3136 * @return {Roo.MessageBox} This message box
3138 updateText : function(text)
3140 if(!dlg.isVisible() && !opt.width){
3141 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3144 msgEl.innerHTML = text || ' ';
3146 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3149 Math.min(opt.width || cw , this.maxWidth),
3150 Math.max(opt.minWidth || this.minWidth, bwidth)
3153 activeTextEl.setWidth(w);
3155 if(dlg.isVisible()){
3156 dlg.fixedcenter = false;
3158 // to big, make it scroll. = But as usual stupid IE does not support
3161 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3165 bodyEl.dom.style.height = '';
3166 bodyEl.dom.style.overflowY = '';
3169 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3171 bodyEl.dom.style.overflowX = '';
3174 dlg.setContentSize(w, bodyEl.getHeight());
3175 if(dlg.isVisible()){
3176 dlg.fixedcenter = true;
3182 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3183 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186 * @return {Roo.MessageBox} This message box
3188 updateProgress : function(value, text){
3190 this.updateText(text);
3192 if (pp) { // weird bug on my firefox - for some reason this is not defined
3193 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3199 * Returns true if the message box is currently displayed
3200 * @return {Boolean} True if the message box is visible, else false
3202 isVisible : function(){
3203 return dlg && dlg.isVisible();
3207 * Hides the message box if it is displayed
3210 if(this.isVisible()){
3216 * Displays a new message box, or reinitializes an existing message box, based on the config options
3217 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218 * The following config object properties are supported:
3220 Property Type Description
3221 ---------- --------------- ------------------------------------------------------------------------------------
3222 animEl String/Element An id or Element from which the message box should animate as it opens and
3223 closes (defaults to undefined)
3224 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable Boolean False to hide the top-right close button (defaults to true). Note that
3227 progress and wait dialogs will ignore this property and always hide the
3228 close button as they can only be closed programmatically.
3229 cls String A custom CSS class to apply to the message box element
3230 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3231 displayed (defaults to 75)
3232 fn Function A callback function to execute after closing the dialog. The arguments to the
3233 function will be btn (the name of the button that was clicked, if applicable,
3234 e.g. "ok"), and text (the value of the active text field, if applicable).
3235 Progress and wait dialogs will ignore this option since they do not respond to
3236 user actions and can only be closed programmatically, so any required function
3237 should be called by the same code after it closes the dialog.
3238 icon String A CSS class that provides a background image to be used as an icon for
3239 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3241 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3242 modal Boolean False to allow user interaction with the page while the message box is
3243 displayed (defaults to true)
3244 msg String A string that will replace the existing message box body text (defaults
3245 to the XHTML-compliant non-breaking space character ' ')
3246 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3247 progress Boolean True to display a progress bar (defaults to false)
3248 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3251 title String The title text
3252 value String The string value to set into the active textbox element if displayed
3253 wait Boolean True to display a progress bar (defaults to false)
3254 width Number The width of the dialog in pixels
3261 msg: 'Please enter your address:',
3263 buttons: Roo.MessageBox.OKCANCEL,
3266 animEl: 'addAddressBtn'
3269 * @param {Object} config Configuration options
3270 * @return {Roo.MessageBox} This message box
3272 show : function(options)
3275 // this causes nightmares if you show one dialog after another
3276 // especially on callbacks..
3278 if(this.isVisible()){
3281 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3283 Roo.log("New Dialog Message:" + options.msg )
3284 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3288 var d = this.getDialog();
3290 d.setTitle(opt.title || " ");
3291 d.closeEl.setDisplayed(opt.closable !== false);
3292 activeTextEl = textboxEl;
3293 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3298 textareaEl.setHeight(typeof opt.multiline == "number" ?
3299 opt.multiline : this.defaultTextHeight);
3300 activeTextEl = textareaEl;
3309 progressEl.setDisplayed(opt.progress === true);
3310 this.updateProgress(0);
3311 activeTextEl.dom.value = opt.value || "";
3313 dlg.setDefaultButton(activeTextEl);
3315 var bs = opt.buttons;
3319 }else if(bs && bs.yes){
3320 db = buttons["yes"];
3322 dlg.setDefaultButton(db);
3324 bwidth = updateButtons(opt.buttons);
3325 this.updateText(opt.msg);
3327 d.el.addClass(opt.cls);
3329 d.proxyDrag = opt.proxyDrag === true;
3330 d.modal = opt.modal !== false;
3331 d.mask = opt.modal !== false ? mask : false;
3333 // force it to the end of the z-index stack so it gets a cursor in FF
3334 document.body.appendChild(dlg.el.dom);
3335 d.animateTarget = null;
3336 d.show(options.animEl);
3342 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3343 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344 * and closing the message box when the process is complete.
3345 * @param {String} title The title bar text
3346 * @param {String} msg The message box body text
3347 * @return {Roo.MessageBox} This message box
3349 progress : function(title, msg){
3356 minWidth: this.minProgressWidth,
3363 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364 * If a callback function is passed it will be called after the user clicks the button, and the
3365 * id of the button that was clicked will be passed as the only parameter to the callback
3366 * (could also be the top-right close button).
3367 * @param {String} title The title bar text
3368 * @param {String} msg The message box body text
3369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370 * @param {Object} scope (optional) The scope of the callback function
3371 * @return {Roo.MessageBox} This message box
3373 alert : function(title, msg, fn, scope)
3388 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3389 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390 * You are responsible for closing the message box when the process is complete.
3391 * @param {String} msg The message box body text
3392 * @param {String} title (optional) The title bar text
3393 * @return {Roo.MessageBox} This message box
3395 wait : function(msg, title){
3406 waitTimer = Roo.TaskMgr.start({
3408 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3416 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419 * @param {String} title The title bar text
3420 * @param {String} msg The message box body text
3421 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422 * @param {Object} scope (optional) The scope of the callback function
3423 * @return {Roo.MessageBox} This message box
3425 confirm : function(title, msg, fn, scope){
3429 buttons: this.YESNO,
3438 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3440 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441 * (could also be the top-right close button) and the text that was entered will be passed as the two
3442 * parameters to the callback.
3443 * @param {String} title The title bar text
3444 * @param {String} msg The message box body text
3445 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446 * @param {Object} scope (optional) The scope of the callback function
3447 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449 * @return {Roo.MessageBox} This message box
3451 prompt : function(title, msg, fn, scope, multiline){
3455 buttons: this.OKCANCEL,
3460 multiline: multiline,
3467 * Button config that displays a single OK button
3472 * Button config that displays Yes and No buttons
3475 YESNO : {yes:true, no:true},
3477 * Button config that displays OK and Cancel buttons
3480 OKCANCEL : {ok:true, cancel:true},
3482 * Button config that displays Yes, No and Cancel buttons
3485 YESNOCANCEL : {yes:true, no:true, cancel:true},
3488 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3491 defaultTextHeight : 75,
3493 * The maximum width in pixels of the message box (defaults to 600)
3498 * The minimum width in pixels of the message box (defaults to 100)
3503 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3504 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3507 minProgressWidth : 250,
3509 * An object containing the default button text strings that can be overriden for localized language support.
3510 * Supported properties are: ok, cancel, yes and no.
3511 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3524 * Shorthand for {@link Roo.MessageBox}
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3536 * @class Roo.bootstrap.Navbar
3537 * @extends Roo.bootstrap.Component
3538 * Bootstrap Navbar class
3541 * Create a new Navbar
3542 * @param {Object} config The config object
3546 Roo.bootstrap.Navbar = function(config){
3547 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3551 * @event beforetoggle
3552 * Fire before toggle the menu
3553 * @param {Roo.EventObject} e
3555 "beforetoggle" : true
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3568 getAutoCreate : function(){
3571 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3575 initEvents :function ()
3577 //Roo.log(this.el.select('.navbar-toggle',true));
3578 this.el.select('.navbar-toggle',true).on('click', function() {
3579 if(this.fireEvent('beforetoggle', this) !== false){
3580 this.el.select('.navbar-collapse',true).toggleClass('in');
3590 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3592 var size = this.el.getSize();
3593 this.maskEl.setSize(size.width, size.height);
3594 this.maskEl.enableDisplayMode("block");
3603 getChildContainer : function()
3605 if (this.el.select('.collapse').getCount()) {
3606 return this.el.select('.collapse',true).first();
3639 * @class Roo.bootstrap.NavSimplebar
3640 * @extends Roo.bootstrap.Navbar
3641 * Bootstrap Sidebar class
3643 * @cfg {Boolean} inverse is inverted color
3645 * @cfg {String} type (nav | pills | tabs)
3646 * @cfg {Boolean} arrangement stacked | justified
3647 * @cfg {String} align (left | right) alignment
3649 * @cfg {Boolean} main (true|false) main nav bar? default false
3650 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3652 * @cfg {String} tag (header|footer|nav|div) default is nav
3658 * Create a new Sidebar
3659 * @param {Object} config The config object
3663 Roo.bootstrap.NavSimplebar = function(config){
3664 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3683 getAutoCreate : function(){
3687 tag : this.tag || 'div',
3700 this.type = this.type || 'nav';
3701 if (['tabs','pills'].indexOf(this.type)!==-1) {
3702 cfg.cn[0].cls += ' nav-' + this.type
3706 if (this.type!=='nav') {
3707 Roo.log('nav type must be nav/tabs/pills')
3709 cfg.cn[0].cls += ' navbar-nav'
3715 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716 cfg.cn[0].cls += ' nav-' + this.arrangement;
3720 if (this.align === 'right') {
3721 cfg.cn[0].cls += ' navbar-right';
3725 cfg.cls += ' navbar-inverse';
3752 * @class Roo.bootstrap.NavHeaderbar
3753 * @extends Roo.bootstrap.NavSimplebar
3754 * Bootstrap Sidebar class
3756 * @cfg {String} brand what is brand
3757 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758 * @cfg {String} brand_href href of the brand
3759 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3760 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3765 * Create a new Sidebar
3766 * @param {Object} config The config object
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3782 desktopCenter : false,
3785 getAutoCreate : function(){
3788 tag: this.nav || 'nav',
3795 if (this.desktopCenter) {
3796 cn.push({cls : 'container', cn : []});
3803 cls: 'navbar-header',
3808 cls: 'navbar-toggle',
3809 'data-toggle': 'collapse',
3814 html: 'Toggle navigation'
3836 cls: 'collapse navbar-collapse',
3840 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3842 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843 cfg.cls += ' navbar-' + this.position;
3845 // tag can override this..
3847 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3850 if (this.brand !== '') {
3853 href: this.brand_href ? this.brand_href : '#',
3854 cls: 'navbar-brand',
3862 cfg.cls += ' main-nav';
3870 getHeaderChildContainer : function()
3872 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873 return this.el.select('.navbar-header',true).first();
3876 return this.getChildContainer();
3880 initEvents : function()
3882 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3884 if (this.autohide) {
3889 Roo.get(document).on('scroll',function(e) {
3890 var ns = Roo.get(document).getScroll().top;
3891 var os = prevScroll;
3895 ft.removeClass('slideDown');
3896 ft.addClass('slideUp');
3899 ft.removeClass('slideUp');
3900 ft.addClass('slideDown');
3921 * @class Roo.bootstrap.NavSidebar
3922 * @extends Roo.bootstrap.Navbar
3923 * Bootstrap Sidebar class
3926 * Create a new Sidebar
3927 * @param {Object} config The config object
3931 Roo.bootstrap.NavSidebar = function(config){
3932 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3937 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3939 getAutoCreate : function(){
3944 cls: 'sidebar sidebar-nav'
3966 * @class Roo.bootstrap.NavGroup
3967 * @extends Roo.bootstrap.Component
3968 * Bootstrap NavGroup class
3969 * @cfg {String} align (left|right)
3970 * @cfg {Boolean} inverse
3971 * @cfg {String} type (nav|pills|tab) default nav
3972 * @cfg {String} navId - reference Id for navbar.
3976 * Create a new nav group
3977 * @param {Object} config The config object
3980 Roo.bootstrap.NavGroup = function(config){
3981 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3984 Roo.bootstrap.NavGroup.register(this);
3988 * Fires when the active item changes
3989 * @param {Roo.bootstrap.NavGroup} this
3990 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4009 getAutoCreate : function()
4011 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4018 if (['tabs','pills'].indexOf(this.type)!==-1) {
4019 cfg.cls += ' nav-' + this.type
4021 if (this.type!=='nav') {
4022 Roo.log('nav type must be nav/tabs/pills')
4024 cfg.cls += ' navbar-nav'
4027 if (this.parent() && this.parent().sidebar) {
4030 cls: 'dashboard-menu sidebar-menu'
4036 if (this.form === true) {
4042 if (this.align === 'right') {
4043 cfg.cls += ' navbar-right';
4045 cfg.cls += ' navbar-left';
4049 if (this.align === 'right') {
4050 cfg.cls += ' navbar-right';
4054 cfg.cls += ' navbar-inverse';
4062 * sets the active Navigation item
4063 * @param {Roo.bootstrap.NavItem} the new current navitem
4065 setActiveItem : function(item)
4068 Roo.each(this.navItems, function(v){
4073 v.setActive(false, true);
4080 item.setActive(true, true);
4081 this.fireEvent('changed', this, item, prev);
4086 * gets the active Navigation item
4087 * @return {Roo.bootstrap.NavItem} the current navitem
4089 getActive : function()
4093 Roo.each(this.navItems, function(v){
4104 indexOfNav : function()
4108 Roo.each(this.navItems, function(v,i){
4119 * adds a Navigation item
4120 * @param {Roo.bootstrap.NavItem} the navitem to add
4122 addItem : function(cfg)
4124 var cn = new Roo.bootstrap.NavItem(cfg);
4126 cn.parentId = this.id;
4127 cn.onRender(this.el, null);
4131 * register a Navigation item
4132 * @param {Roo.bootstrap.NavItem} the navitem to add
4134 register : function(item)
4136 this.navItems.push( item);
4137 item.navId = this.navId;
4142 * clear all the Navigation item
4145 clearAll : function()
4148 this.el.dom.innerHTML = '';
4151 getNavItem: function(tabId)
4154 Roo.each(this.navItems, function(e) {
4155 if (e.tabId == tabId) {
4165 setActiveNext : function()
4167 var i = this.indexOfNav(this.getActive());
4168 if (i > this.navItems.length) {
4171 this.setActiveItem(this.navItems[i+1]);
4173 setActivePrev : function()
4175 var i = this.indexOfNav(this.getActive());
4179 this.setActiveItem(this.navItems[i-1]);
4181 clearWasActive : function(except) {
4182 Roo.each(this.navItems, function(e) {
4183 if (e.tabId != except.tabId && e.was_active) {
4184 e.was_active = false;
4191 getWasActive : function ()
4194 Roo.each(this.navItems, function(e) {
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4213 * register a Navigation Group
4214 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4216 register : function(navgrp)
4218 this.groups[navgrp.navId] = navgrp;
4222 * fetch a Navigation Group based on the navigation ID
4223 * @param {string} the navgroup to add
4224 * @returns {Roo.bootstrap.NavGroup} the navgroup
4226 get: function(navId) {
4227 if (typeof(this.groups[navId]) == 'undefined') {
4229 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4231 return this.groups[navId] ;
4246 * @class Roo.bootstrap.NavItem
4247 * @extends Roo.bootstrap.Component
4248 * Bootstrap Navbar.NavItem class
4249 * @cfg {String} href link to
4250 * @cfg {String} html content of button
4251 * @cfg {String} badge text inside badge
4252 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253 * @cfg {String} glyphicon name of glyphicon
4254 * @cfg {String} icon name of font awesome icon
4255 * @cfg {Boolean} active Is item active
4256 * @cfg {Boolean} disabled Is item disabled
4258 * @cfg {Boolean} preventDefault (true | false) default false
4259 * @cfg {String} tabId the tab that this item activates.
4260 * @cfg {String} tagtype (a|span) render as a href or span?
4261 * @cfg {Boolean} animateRef (true|false) link to element default false
4264 * Create a new Navbar Item
4265 * @param {Object} config The config object
4267 Roo.bootstrap.NavItem = function(config){
4268 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4273 * The raw click event for the entire grid.
4274 * @param {Roo.EventObject} e
4279 * Fires when the active item active state changes
4280 * @param {Roo.bootstrap.NavItem} this
4281 * @param {boolean} state the new state
4287 * Fires when scroll to element
4288 * @param {Roo.bootstrap.NavItem} this
4289 * @param {Object} options
4290 * @param {Roo.EventObject} e
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4306 preventDefault : false,
4313 getAutoCreate : function(){
4322 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4324 if (this.disabled) {
4325 cfg.cls += ' disabled';
4328 if (this.href || this.html || this.glyphicon || this.icon) {
4332 href : this.href || "#",
4333 html: this.html || ''
4338 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4341 if(this.glyphicon) {
4342 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4347 cfg.cn[0].html += " <span class='caret'></span>";
4351 if (this.badge !== '') {
4353 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4361 initEvents: function()
4363 if (typeof (this.menu) != 'undefined') {
4364 this.menu.parentType = this.xtype;
4365 this.menu.triggerEl = this.el;
4366 this.menu = this.addxtype(Roo.apply({}, this.menu));
4369 this.el.select('a',true).on('click', this.onClick, this);
4371 if(this.tagtype == 'span'){
4372 this.el.select('span',true).on('click', this.onClick, this);
4375 // at this point parent should be available..
4376 this.parent().register(this);
4379 onClick : function(e)
4381 if (e.getTarget('.dropdown-menu-item')) {
4382 // did you click on a menu itemm.... - then don't trigger onclick..
4387 this.preventDefault ||
4390 Roo.log("NavItem - prevent Default?");
4394 if (this.disabled) {
4398 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399 if (tg && tg.transition) {
4400 Roo.log("waiting for the transitionend");
4406 //Roo.log("fire event clicked");
4407 if(this.fireEvent('click', this, e) === false){
4411 if(this.tagtype == 'span'){
4415 //Roo.log(this.href);
4416 var ael = this.el.select('a',true).first();
4419 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422 return; // ignore... - it's a 'hash' to another page.
4424 Roo.log("NavItem - prevent Default?");
4426 this.scrollToElement(e);
4430 var p = this.parent();
4432 if (['tabs','pills'].indexOf(p.type)!==-1) {
4433 if (typeof(p.setActiveItem) !== 'undefined') {
4434 p.setActiveItem(this);
4438 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440 // remove the collapsed menu expand...
4441 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4445 isActive: function () {
4448 setActive : function(state, fire, is_was_active)
4450 if (this.active && !state && this.navId) {
4451 this.was_active = true;
4452 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454 nv.clearWasActive(this);
4458 this.active = state;
4461 this.el.removeClass('active');
4462 } else if (!this.el.hasClass('active')) {
4463 this.el.addClass('active');
4466 this.fireEvent('changed', this, state);
4469 // show a panel if it's registered and related..
4471 if (!this.navId || !this.tabId || !state || is_was_active) {
4475 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4479 var pan = tg.getPanelByName(this.tabId);
4483 // if we can not flip to new panel - go back to old nav highlight..
4484 if (false == tg.showPanel(pan)) {
4485 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4487 var onav = nv.getWasActive();
4489 onav.setActive(true, false, true);
4498 // this should not be here...
4499 setDisabled : function(state)
4501 this.disabled = state;
4503 this.el.removeClass('disabled');
4504 } else if (!this.el.hasClass('disabled')) {
4505 this.el.addClass('disabled');
4511 * Fetch the element to display the tooltip on.
4512 * @return {Roo.Element} defaults to this.el
4514 tooltipEl : function()
4516 return this.el.select('' + this.tagtype + '', true).first();
4519 scrollToElement : function(e)
4521 var c = document.body;
4524 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4526 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527 c = document.documentElement;
4530 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4536 var o = target.calcOffsetsTo(c);
4543 this.fireEvent('scrollto', this, options, e);
4545 Roo.get(c).scrollTo('top', options.value, true);
4558 * <span> icon </span>
4559 * <span> text </span>
4560 * <span>badge </span>
4564 * @class Roo.bootstrap.NavSidebarItem
4565 * @extends Roo.bootstrap.NavItem
4566 * Bootstrap Navbar.NavSidebarItem class
4567 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568 * {Boolean} open is the menu open
4569 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571 * {String} buttonSize (sm|md|lg)the extra classes for the button
4572 * {Boolean} showArrow show arrow next to the text (default true)
4574 * Create a new Navbar Button
4575 * @param {Object} config The config object
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4583 * The raw click event for the entire grid.
4584 * @param {Roo.EventObject} e
4589 * Fires when the active item active state changes
4590 * @param {Roo.bootstrap.NavSidebarItem} this
4591 * @param {boolean} state the new state
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4601 badgeWeight : 'default',
4607 buttonWeight : 'default',
4613 getAutoCreate : function(){
4618 href : this.href || '#',
4624 if(this.buttonView){
4627 href : this.href || '#',
4628 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4641 cfg.cls += ' active';
4644 if (this.disabled) {
4645 cfg.cls += ' disabled';
4648 cfg.cls += ' open x-open';
4651 if (this.glyphicon || this.icon) {
4652 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4653 a.cn.push({ tag : 'i', cls : c }) ;
4656 if(!this.buttonView){
4659 html : this.html || ''
4666 if (this.badge !== '') {
4667 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4673 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4676 a.cls += ' dropdown-toggle treeview' ;
4682 initEvents : function()
4684 if (typeof (this.menu) != 'undefined') {
4685 this.menu.parentType = this.xtype;
4686 this.menu.triggerEl = this.el;
4687 this.menu = this.addxtype(Roo.apply({}, this.menu));
4690 this.el.on('click', this.onClick, this);
4692 if(this.badge !== ''){
4693 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4698 onClick : function(e)
4705 if(this.preventDefault){
4709 this.fireEvent('click', this);
4712 disable : function()
4714 this.setDisabled(true);
4719 this.setDisabled(false);
4722 setDisabled : function(state)
4724 if(this.disabled == state){
4728 this.disabled = state;
4731 this.el.addClass('disabled');
4735 this.el.removeClass('disabled');
4740 setActive : function(state)
4742 if(this.active == state){
4746 this.active = state;
4749 this.el.addClass('active');
4753 this.el.removeClass('active');
4758 isActive: function ()
4763 setBadge : function(str)
4769 this.badgeEl.dom.innerHTML = str;
4786 * @class Roo.bootstrap.Row
4787 * @extends Roo.bootstrap.Component
4788 * Bootstrap Row class (contains columns...)
4792 * @param {Object} config The config object
4795 Roo.bootstrap.Row = function(config){
4796 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4801 getAutoCreate : function(){
4820 * @class Roo.bootstrap.Element
4821 * @extends Roo.bootstrap.Component
4822 * Bootstrap Element class
4823 * @cfg {String} html contents of the element
4824 * @cfg {String} tag tag of the element
4825 * @cfg {String} cls class of the element
4826 * @cfg {Boolean} preventDefault (true|false) default false
4827 * @cfg {Boolean} clickable (true|false) default false
4830 * Create a new Element
4831 * @param {Object} config The config object
4834 Roo.bootstrap.Element = function(config){
4835 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4841 * When a element is chick
4842 * @param {Roo.bootstrap.Element} this
4843 * @param {Roo.EventObject} e
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4854 preventDefault: false,
4857 getAutoCreate : function(){
4868 initEvents: function()
4870 Roo.bootstrap.Element.superclass.initEvents.call(this);
4873 this.el.on('click', this.onClick, this);
4878 onClick : function(e)
4880 if(this.preventDefault){
4884 this.fireEvent('click', this, e);
4887 getValue : function()
4889 return this.el.dom.innerHTML;
4892 setValue : function(value)
4894 this.el.dom.innerHTML = value;
4909 * @class Roo.bootstrap.Pagination
4910 * @extends Roo.bootstrap.Component
4911 * Bootstrap Pagination class
4912 * @cfg {String} size xs | sm | md | lg
4913 * @cfg {Boolean} inverse false | true
4916 * Create a new Pagination
4917 * @param {Object} config The config object
4920 Roo.bootstrap.Pagination = function(config){
4921 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4930 getAutoCreate : function(){
4936 cfg.cls += ' inverse';
4942 cfg.cls += " " + this.cls;
4960 * @class Roo.bootstrap.PaginationItem
4961 * @extends Roo.bootstrap.Component
4962 * Bootstrap PaginationItem class
4963 * @cfg {String} html text
4964 * @cfg {String} href the link
4965 * @cfg {Boolean} preventDefault (true | false) default true
4966 * @cfg {Boolean} active (true | false) default false
4967 * @cfg {Boolean} disabled default false
4971 * Create a new PaginationItem
4972 * @param {Object} config The config object
4976 Roo.bootstrap.PaginationItem = function(config){
4977 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4982 * The raw click event for the entire grid.
4983 * @param {Roo.EventObject} e
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4993 preventDefault: true,
4998 getAutoCreate : function(){
5004 href : this.href ? this.href : '#',
5005 html : this.html ? this.html : ''
5015 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5019 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5025 initEvents: function() {
5027 this.el.on('click', this.onClick, this);
5030 onClick : function(e)
5032 Roo.log('PaginationItem on click ');
5033 if(this.preventDefault){
5041 this.fireEvent('click', this, e);
5057 * @class Roo.bootstrap.Slider
5058 * @extends Roo.bootstrap.Component
5059 * Bootstrap Slider class
5062 * Create a new Slider
5063 * @param {Object} config The config object
5066 Roo.bootstrap.Slider = function(config){
5067 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5072 getAutoCreate : function(){
5076 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5080 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5092 * Ext JS Library 1.1.1
5093 * Copyright(c) 2006-2007, Ext JS, LLC.
5095 * Originally Released Under LGPL - original licence link has changed is not relivant.
5098 * <script type="text/javascript">
5103 * @class Roo.grid.ColumnModel
5104 * @extends Roo.util.Observable
5105 * This is the default implementation of a ColumnModel used by the Grid. It defines
5106 * the columns in the grid.
5109 var colModel = new Roo.grid.ColumnModel([
5110 {header: "Ticker", width: 60, sortable: true, locked: true},
5111 {header: "Company Name", width: 150, sortable: true},
5112 {header: "Market Cap.", width: 100, sortable: true},
5113 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114 {header: "Employees", width: 100, sortable: true, resizable: false}
5119 * The config options listed for this class are options which may appear in each
5120 * individual column definition.
5121 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5123 * @param {Object} config An Array of column config objects. See this class's
5124 * config objects for details.
5126 Roo.grid.ColumnModel = function(config){
5128 * The config passed into the constructor
5130 this.config = config;
5133 // if no id, create one
5134 // if the column does not have a dataIndex mapping,
5135 // map it to the order it is in the config
5136 for(var i = 0, len = config.length; i < len; i++){
5138 if(typeof c.dataIndex == "undefined"){
5141 if(typeof c.renderer == "string"){
5142 c.renderer = Roo.util.Format[c.renderer];
5144 if(typeof c.id == "undefined"){
5147 if(c.editor && c.editor.xtype){
5148 c.editor = Roo.factory(c.editor, Roo.grid);
5150 if(c.editor && c.editor.isFormField){
5151 c.editor = new Roo.grid.GridEditor(c.editor);
5153 this.lookup[c.id] = c;
5157 * The width of columns which have no width specified (defaults to 100)
5160 this.defaultWidth = 100;
5163 * Default sortable of columns which have no sortable specified (defaults to false)
5166 this.defaultSortable = false;
5170 * @event widthchange
5171 * Fires when the width of a column changes.
5172 * @param {ColumnModel} this
5173 * @param {Number} columnIndex The column index
5174 * @param {Number} newWidth The new width
5176 "widthchange": true,
5178 * @event headerchange
5179 * Fires when the text of a header changes.
5180 * @param {ColumnModel} this
5181 * @param {Number} columnIndex The column index
5182 * @param {Number} newText The new header text
5184 "headerchange": true,
5186 * @event hiddenchange
5187 * Fires when a column is hidden or "unhidden".
5188 * @param {ColumnModel} this
5189 * @param {Number} columnIndex The column index
5190 * @param {Boolean} hidden true if hidden, false otherwise
5192 "hiddenchange": true,
5194 * @event columnmoved
5195 * Fires when a column is moved.
5196 * @param {ColumnModel} this
5197 * @param {Number} oldIndex
5198 * @param {Number} newIndex
5200 "columnmoved" : true,
5202 * @event columlockchange
5203 * Fires when a column's locked state is changed
5204 * @param {ColumnModel} this
5205 * @param {Number} colIndex
5206 * @param {Boolean} locked true if locked
5208 "columnlockchange" : true
5210 Roo.grid.ColumnModel.superclass.constructor.call(this);
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5214 * @cfg {String} header The header text to display in the Grid view.
5217 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219 * specified, the column's index is used as an index into the Record's data Array.
5222 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5226 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227 * Defaults to the value of the {@link #defaultSortable} property.
5228 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5231 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5234 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5237 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5240 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5243 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5249 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5252 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5255 * @cfg {String} cursor (Optional)
5258 * @cfg {String} tooltip (Optional)
5261 * @cfg {Number} xs (Optional)
5264 * @cfg {Number} sm (Optional)
5267 * @cfg {Number} md (Optional)
5270 * @cfg {Number} lg (Optional)
5273 * Returns the id of the column at the specified index.
5274 * @param {Number} index The column index
5275 * @return {String} the id
5277 getColumnId : function(index){
5278 return this.config[index].id;
5282 * Returns the column for a specified id.
5283 * @param {String} id The column id
5284 * @return {Object} the column
5286 getColumnById : function(id){
5287 return this.lookup[id];
5292 * Returns the column for a specified dataIndex.
5293 * @param {String} dataIndex The column dataIndex
5294 * @return {Object|Boolean} the column or false if not found
5296 getColumnByDataIndex: function(dataIndex){
5297 var index = this.findColumnIndex(dataIndex);
5298 return index > -1 ? this.config[index] : false;
5302 * Returns the index for a specified column id.
5303 * @param {String} id The column id
5304 * @return {Number} the index, or -1 if not found
5306 getIndexById : function(id){
5307 for(var i = 0, len = this.config.length; i < len; i++){
5308 if(this.config[i].id == id){
5316 * Returns the index for a specified column dataIndex.
5317 * @param {String} dataIndex The column dataIndex
5318 * @return {Number} the index, or -1 if not found
5321 findColumnIndex : function(dataIndex){
5322 for(var i = 0, len = this.config.length; i < len; i++){
5323 if(this.config[i].dataIndex == dataIndex){
5331 moveColumn : function(oldIndex, newIndex){
5332 var c = this.config[oldIndex];
5333 this.config.splice(oldIndex, 1);
5334 this.config.splice(newIndex, 0, c);
5335 this.dataMap = null;
5336 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5339 isLocked : function(colIndex){
5340 return this.config[colIndex].locked === true;
5343 setLocked : function(colIndex, value, suppressEvent){
5344 if(this.isLocked(colIndex) == value){
5347 this.config[colIndex].locked = value;
5349 this.fireEvent("columnlockchange", this, colIndex, value);
5353 getTotalLockedWidth : function(){
5355 for(var i = 0; i < this.config.length; i++){
5356 if(this.isLocked(i) && !this.isHidden(i)){
5357 this.totalWidth += this.getColumnWidth(i);
5363 getLockedCount : function(){
5364 for(var i = 0, len = this.config.length; i < len; i++){
5365 if(!this.isLocked(i)){
5370 return this.config.length;
5374 * Returns the number of columns.
5377 getColumnCount : function(visibleOnly){
5378 if(visibleOnly === true){
5380 for(var i = 0, len = this.config.length; i < len; i++){
5381 if(!this.isHidden(i)){
5387 return this.config.length;
5391 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392 * @param {Function} fn
5393 * @param {Object} scope (optional)
5394 * @return {Array} result
5396 getColumnsBy : function(fn, scope){
5398 for(var i = 0, len = this.config.length; i < len; i++){
5399 var c = this.config[i];
5400 if(fn.call(scope||this, c, i) === true){
5408 * Returns true if the specified column is sortable.
5409 * @param {Number} col The column index
5412 isSortable : function(col){
5413 if(typeof this.config[col].sortable == "undefined"){
5414 return this.defaultSortable;
5416 return this.config[col].sortable;
5420 * Returns the rendering (formatting) function defined for the column.
5421 * @param {Number} col The column index.
5422 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5424 getRenderer : function(col){
5425 if(!this.config[col].renderer){
5426 return Roo.grid.ColumnModel.defaultRenderer;
5428 return this.config[col].renderer;
5432 * Sets the rendering (formatting) function for a column.
5433 * @param {Number} col The column index
5434 * @param {Function} fn The function to use to process the cell's raw data
5435 * to return HTML markup for the grid view. The render function is called with
5436 * the following parameters:<ul>
5437 * <li>Data value.</li>
5438 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439 * <li>css A CSS style string to apply to the table cell.</li>
5440 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442 * <li>Row index</li>
5443 * <li>Column index</li>
5444 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5446 setRenderer : function(col, fn){
5447 this.config[col].renderer = fn;
5451 * Returns the width for the specified column.
5452 * @param {Number} col The column index
5455 getColumnWidth : function(col){
5456 return this.config[col].width * 1 || this.defaultWidth;
5460 * Sets the width for a column.
5461 * @param {Number} col The column index
5462 * @param {Number} width The new width
5464 setColumnWidth : function(col, width, suppressEvent){
5465 this.config[col].width = width;
5466 this.totalWidth = null;
5468 this.fireEvent("widthchange", this, col, width);
5473 * Returns the total width of all columns.
5474 * @param {Boolean} includeHidden True to include hidden column widths
5477 getTotalWidth : function(includeHidden){
5478 if(!this.totalWidth){
5479 this.totalWidth = 0;
5480 for(var i = 0, len = this.config.length; i < len; i++){
5481 if(includeHidden || !this.isHidden(i)){
5482 this.totalWidth += this.getColumnWidth(i);
5486 return this.totalWidth;
5490 * Returns the header for the specified column.
5491 * @param {Number} col The column index
5494 getColumnHeader : function(col){
5495 return this.config[col].header;
5499 * Sets the header for a column.
5500 * @param {Number} col The column index
5501 * @param {String} header The new header
5503 setColumnHeader : function(col, header){
5504 this.config[col].header = header;
5505 this.fireEvent("headerchange", this, col, header);
5509 * Returns the tooltip for the specified column.
5510 * @param {Number} col The column index
5513 getColumnTooltip : function(col){
5514 return this.config[col].tooltip;
5517 * Sets the tooltip for a column.
5518 * @param {Number} col The column index
5519 * @param {String} tooltip The new tooltip
5521 setColumnTooltip : function(col, tooltip){
5522 this.config[col].tooltip = tooltip;
5526 * Returns the dataIndex for the specified column.
5527 * @param {Number} col The column index
5530 getDataIndex : function(col){
5531 return this.config[col].dataIndex;
5535 * Sets the dataIndex for a column.
5536 * @param {Number} col The column index
5537 * @param {Number} dataIndex The new dataIndex
5539 setDataIndex : function(col, dataIndex){
5540 this.config[col].dataIndex = dataIndex;
5546 * Returns true if the cell is editable.
5547 * @param {Number} colIndex The column index
5548 * @param {Number} rowIndex The row index - this is nto actually used..?
5551 isCellEditable : function(colIndex, rowIndex){
5552 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5556 * Returns the editor defined for the cell/column.
5557 * return false or null to disable editing.
5558 * @param {Number} colIndex The column index
5559 * @param {Number} rowIndex The row index
5562 getCellEditor : function(colIndex, rowIndex){
5563 return this.config[colIndex].editor;
5567 * Sets if a column is editable.
5568 * @param {Number} col The column index
5569 * @param {Boolean} editable True if the column is editable
5571 setEditable : function(col, editable){
5572 this.config[col].editable = editable;
5577 * Returns true if the column is hidden.
5578 * @param {Number} colIndex The column index
5581 isHidden : function(colIndex){
5582 return this.config[colIndex].hidden;
5587 * Returns true if the column width cannot be changed
5589 isFixed : function(colIndex){
5590 return this.config[colIndex].fixed;
5594 * Returns true if the column can be resized
5597 isResizable : function(colIndex){
5598 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5601 * Sets if a column is hidden.
5602 * @param {Number} colIndex The column index
5603 * @param {Boolean} hidden True if the column is hidden
5605 setHidden : function(colIndex, hidden){
5606 this.config[colIndex].hidden = hidden;
5607 this.totalWidth = null;
5608 this.fireEvent("hiddenchange", this, colIndex, hidden);
5612 * Sets the editor for a column.
5613 * @param {Number} col The column index
5614 * @param {Object} editor The editor object
5616 setEditor : function(col, editor){
5617 this.config[col].editor = editor;
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5623 if(typeof value == "object") {
5626 if(typeof value == "string" && value.length < 1){
5630 return String.format("{0}", value);
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5637 * Ext JS Library 1.1.1
5638 * Copyright(c) 2006-2007, Ext JS, LLC.
5640 * Originally Released Under LGPL - original licence link has changed is not relivant.
5643 * <script type="text/javascript">
5647 * @class Roo.LoadMask
5648 * A simple utility class for generically masking elements while loading data. If the element being masked has
5649 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5651 * element's UpdateManager load indicator and will be destroyed after the initial load.
5653 * Create a new LoadMask
5654 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655 * @param {Object} config The config object
5657 Roo.LoadMask = function(el, config){
5658 this.el = Roo.get(el);
5659 Roo.apply(this, config);
5661 this.store.on('beforeload', this.onBeforeLoad, this);
5662 this.store.on('load', this.onLoad, this);
5663 this.store.on('loadexception', this.onLoadException, this);
5664 this.removeMask = false;
5666 var um = this.el.getUpdateManager();
5667 um.showLoadIndicator = false; // disable the default indicator
5668 um.on('beforeupdate', this.onBeforeLoad, this);
5669 um.on('update', this.onLoad, this);
5670 um.on('failure', this.onLoad, this);
5671 this.removeMask = true;
5675 Roo.LoadMask.prototype = {
5677 * @cfg {Boolean} removeMask
5678 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5683 * The text to display in a centered loading message box (defaults to 'Loading...')
5687 * @cfg {String} msgCls
5688 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5690 msgCls : 'x-mask-loading',
5693 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5699 * Disables the mask to prevent it from being displayed
5701 disable : function(){
5702 this.disabled = true;
5706 * Enables the mask so that it can be displayed
5708 enable : function(){
5709 this.disabled = false;
5712 onLoadException : function()
5716 if (typeof(arguments[3]) != 'undefined') {
5717 Roo.MessageBox.alert("Error loading",arguments[3]);
5721 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5729 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5734 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5738 onBeforeLoad : function(){
5740 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5745 destroy : function(){
5747 this.store.un('beforeload', this.onBeforeLoad, this);
5748 this.store.un('load', this.onLoad, this);
5749 this.store.un('loadexception', this.onLoadException, this);
5751 var um = this.el.getUpdateManager();
5752 um.un('beforeupdate', this.onBeforeLoad, this);
5753 um.un('update', this.onLoad, this);
5754 um.un('failure', this.onLoad, this);
5765 * @class Roo.bootstrap.Table
5766 * @extends Roo.bootstrap.Component
5767 * Bootstrap Table class
5768 * @cfg {String} cls table class
5769 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770 * @cfg {String} bgcolor Specifies the background color for a table
5771 * @cfg {Number} border Specifies whether the table cells should have borders or not
5772 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773 * @cfg {Number} cellspacing Specifies the space between cells
5774 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776 * @cfg {String} sortable Specifies that the table should be sortable
5777 * @cfg {String} summary Specifies a summary of the content of a table
5778 * @cfg {Number} width Specifies the width of a table
5779 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5781 * @cfg {boolean} striped Should the rows be alternative striped
5782 * @cfg {boolean} bordered Add borders to the table
5783 * @cfg {boolean} hover Add hover highlighting
5784 * @cfg {boolean} condensed Format condensed
5785 * @cfg {boolean} responsive Format condensed
5786 * @cfg {Boolean} loadMask (true|false) default false
5787 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789 * @cfg {Boolean} rowSelection (true|false) default false
5790 * @cfg {Boolean} cellSelection (true|false) default false
5791 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5793 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5797 * Create a new Table
5798 * @param {Object} config The config object
5801 Roo.bootstrap.Table = function(config){
5802 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5807 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5812 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5814 this.sm.grid = this;
5815 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816 this.sm = this.selModel;
5817 this.sm.xmodule = this.xmodule || false;
5820 if (this.cm && typeof(this.cm.config) == 'undefined') {
5821 this.colModel = new Roo.grid.ColumnModel(this.cm);
5822 this.cm = this.colModel;
5823 this.cm.xmodule = this.xmodule || false;
5826 this.store= Roo.factory(this.store, Roo.data);
5827 this.ds = this.store;
5828 this.ds.xmodule = this.xmodule || false;
5831 if (this.footer && this.store) {
5832 this.footer.dataSource = this.ds;
5833 this.footer = Roo.factory(this.footer);
5840 * Fires when a cell is clicked
5841 * @param {Roo.bootstrap.Table} this
5842 * @param {Roo.Element} el
5843 * @param {Number} rowIndex
5844 * @param {Number} columnIndex
5845 * @param {Roo.EventObject} e
5849 * @event celldblclick
5850 * Fires when a cell is double clicked
5851 * @param {Roo.bootstrap.Table} this
5852 * @param {Roo.Element} el
5853 * @param {Number} rowIndex
5854 * @param {Number} columnIndex
5855 * @param {Roo.EventObject} e
5857 "celldblclick" : true,
5860 * Fires when a row is clicked
5861 * @param {Roo.bootstrap.Table} this
5862 * @param {Roo.Element} el
5863 * @param {Number} rowIndex
5864 * @param {Roo.EventObject} e
5868 * @event rowdblclick
5869 * Fires when a row is double clicked
5870 * @param {Roo.bootstrap.Table} this
5871 * @param {Roo.Element} el
5872 * @param {Number} rowIndex
5873 * @param {Roo.EventObject} e
5875 "rowdblclick" : true,
5878 * Fires when a mouseover occur
5879 * @param {Roo.bootstrap.Table} this
5880 * @param {Roo.Element} el
5881 * @param {Number} rowIndex
5882 * @param {Number} columnIndex
5883 * @param {Roo.EventObject} e
5888 * Fires when a mouseout occur
5889 * @param {Roo.bootstrap.Table} this
5890 * @param {Roo.Element} el
5891 * @param {Number} rowIndex
5892 * @param {Number} columnIndex
5893 * @param {Roo.EventObject} e
5898 * Fires when a row is rendered, so you can change add a style to it.
5899 * @param {Roo.bootstrap.Table} this
5900 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5904 * @event rowsrendered
5905 * Fires when all the rows have been rendered
5906 * @param {Roo.bootstrap.Table} this
5908 'rowsrendered' : true,
5910 * @event contextmenu
5911 * The raw contextmenu event for the entire grid.
5912 * @param {Roo.EventObject} e
5914 "contextmenu" : true,
5916 * @event rowcontextmenu
5917 * Fires when a row is right clicked
5918 * @param {Roo.bootstrap.Table} this
5919 * @param {Number} rowIndex
5920 * @param {Roo.EventObject} e
5922 "rowcontextmenu" : true,
5924 * @event cellcontextmenu
5925 * Fires when a cell is right clicked
5926 * @param {Roo.bootstrap.Table} this
5927 * @param {Number} rowIndex
5928 * @param {Number} cellIndex
5929 * @param {Roo.EventObject} e
5931 "cellcontextmenu" : true,
5933 * @event headercontextmenu
5934 * Fires when a header is right clicked
5935 * @param {Roo.bootstrap.Table} this
5936 * @param {Number} columnIndex
5937 * @param {Roo.EventObject} e
5939 "headercontextmenu" : true
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5969 rowSelection : false,
5970 cellSelection : false,
5973 // Roo.Element - the tbody
5975 // Roo.Element - thead element
5978 container: false, // used by gridpanel...
5982 getAutoCreate : function()
5984 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5991 if (this.scrollBody) {
5992 cfg.cls += ' table-body-fixed';
5995 cfg.cls += ' table-striped';
5999 cfg.cls += ' table-hover';
6001 if (this.bordered) {
6002 cfg.cls += ' table-bordered';
6004 if (this.condensed) {
6005 cfg.cls += ' table-condensed';
6007 if (this.responsive) {
6008 cfg.cls += ' table-responsive';
6012 cfg.cls+= ' ' +this.cls;
6015 // this lot should be simplifed...
6018 cfg.align=this.align;
6021 cfg.bgcolor=this.bgcolor;
6024 cfg.border=this.border;
6026 if (this.cellpadding) {
6027 cfg.cellpadding=this.cellpadding;
6029 if (this.cellspacing) {
6030 cfg.cellspacing=this.cellspacing;
6033 cfg.frame=this.frame;
6036 cfg.rules=this.rules;
6038 if (this.sortable) {
6039 cfg.sortable=this.sortable;
6042 cfg.summary=this.summary;
6045 cfg.width=this.width;
6048 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6051 if(this.store || this.cm){
6052 if(this.headerShow){
6053 cfg.cn.push(this.renderHeader());
6056 cfg.cn.push(this.renderBody());
6058 if(this.footerShow){
6059 cfg.cn.push(this.renderFooter());
6061 // where does this come from?
6062 //cfg.cls+= ' TableGrid';
6065 return { cn : [ cfg ] };
6068 initEvents : function()
6070 if(!this.store || !this.cm){
6073 if (this.selModel) {
6074 this.selModel.initEvents();
6078 //Roo.log('initEvents with ds!!!!');
6080 this.mainBody = this.el.select('tbody', true).first();
6081 this.mainHead = this.el.select('thead', true).first();
6088 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089 e.on('click', _this.sort, _this);
6092 this.mainBody.on("click", this.onClick, this);
6093 this.mainBody.on("dblclick", this.onDblClick, this);
6095 // why is this done????? = it breaks dialogs??
6096 //this.parent().el.setStyle('position', 'relative');
6100 this.footer.parentId = this.id;
6101 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6104 this.el.select('tfoot tr td').first().addClass('hide');
6108 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6110 this.store.on('load', this.onLoad, this);
6111 this.store.on('beforeload', this.onBeforeLoad, this);
6112 this.store.on('update', this.onUpdate, this);
6113 this.store.on('add', this.onAdd, this);
6114 this.store.on("clear", this.clear, this);
6116 this.el.on("contextmenu", this.onContextMenu, this);
6118 this.mainBody.on('scroll', this.onBodyScroll, this);
6123 onContextMenu : function(e, t)
6125 this.processEvent("contextmenu", e);
6128 processEvent : function(name, e)
6130 if (name != 'touchstart' ) {
6131 this.fireEvent(name, e);
6134 var t = e.getTarget();
6136 var cell = Roo.get(t);
6142 if(cell.findParent('tfoot', false, true)){
6146 if(cell.findParent('thead', false, true)){
6148 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149 cell = Roo.get(t).findParent('th', false, true);
6151 Roo.log("failed to find th in thead?");
6152 Roo.log(e.getTarget());
6157 var cellIndex = cell.dom.cellIndex;
6159 var ename = name == 'touchstart' ? 'click' : name;
6160 this.fireEvent("header" + ename, this, cellIndex, e);
6165 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166 cell = Roo.get(t).findParent('td', false, true);
6168 Roo.log("failed to find th in tbody?");
6169 Roo.log(e.getTarget());
6174 var row = cell.findParent('tr', false, true);
6175 var cellIndex = cell.dom.cellIndex;
6176 var rowIndex = row.dom.rowIndex - 1;
6180 this.fireEvent("row" + name, this, rowIndex, e);
6184 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6190 onMouseover : function(e, el)
6192 var cell = Roo.get(el);
6198 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199 cell = cell.findParent('td', false, true);
6202 var row = cell.findParent('tr', false, true);
6203 var cellIndex = cell.dom.cellIndex;
6204 var rowIndex = row.dom.rowIndex - 1; // start from 0
6206 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6210 onMouseout : function(e, el)
6212 var cell = Roo.get(el);
6218 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219 cell = cell.findParent('td', false, true);
6222 var row = cell.findParent('tr', false, true);
6223 var cellIndex = cell.dom.cellIndex;
6224 var rowIndex = row.dom.rowIndex - 1; // start from 0
6226 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6230 onClick : function(e, el)
6232 var cell = Roo.get(el);
6234 if(!cell || (!this.cellSelection && !this.rowSelection)){
6238 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239 cell = cell.findParent('td', false, true);
6242 if(!cell || typeof(cell) == 'undefined'){
6246 var row = cell.findParent('tr', false, true);
6248 if(!row || typeof(row) == 'undefined'){
6252 var cellIndex = cell.dom.cellIndex;
6253 var rowIndex = this.getRowIndex(row);
6255 // why??? - should these not be based on SelectionModel?
6256 if(this.cellSelection){
6257 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6260 if(this.rowSelection){
6261 this.fireEvent('rowclick', this, row, rowIndex, e);
6267 onDblClick : function(e,el)
6269 var cell = Roo.get(el);
6271 if(!cell || (!this.cellSelection && !this.rowSelection)){
6275 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276 cell = cell.findParent('td', false, true);
6279 if(!cell || typeof(cell) == 'undefined'){
6283 var row = cell.findParent('tr', false, true);
6285 if(!row || typeof(row) == 'undefined'){
6289 var cellIndex = cell.dom.cellIndex;
6290 var rowIndex = this.getRowIndex(row);
6292 if(this.cellSelection){
6293 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6296 if(this.rowSelection){
6297 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6301 sort : function(e,el)
6303 var col = Roo.get(el);
6305 if(!col.hasClass('sortable')){
6309 var sort = col.attr('sort');
6312 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6316 this.store.sortInfo = {field : sort, direction : dir};
6319 Roo.log("calling footer first");
6320 this.footer.onClick('first');
6323 this.store.load({ params : { start : 0 } });
6327 renderHeader : function()
6335 this.totalWidth = 0;
6337 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6339 var config = cm.config[i];
6344 html: cm.getColumnHeader(i)
6349 if(typeof(config.sortable) != 'undefined' && config.sortable){
6351 c.html = '<i class="glyphicon"></i>' + c.html;
6354 if(typeof(config.lgHeader) != 'undefined'){
6355 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6358 if(typeof(config.mdHeader) != 'undefined'){
6359 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6362 if(typeof(config.smHeader) != 'undefined'){
6363 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6366 if(typeof(config.xsHeader) != 'undefined'){
6367 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6374 if(typeof(config.tooltip) != 'undefined'){
6375 c.tooltip = config.tooltip;
6378 if(typeof(config.colspan) != 'undefined'){
6379 c.colspan = config.colspan;
6382 if(typeof(config.hidden) != 'undefined' && config.hidden){
6383 c.style += ' display:none;';
6386 if(typeof(config.dataIndex) != 'undefined'){
6387 c.sort = config.dataIndex;
6392 if(typeof(config.align) != 'undefined' && config.align.length){
6393 c.style += ' text-align:' + config.align + ';';
6396 if(typeof(config.width) != 'undefined'){
6397 c.style += ' width:' + config.width + 'px;';
6398 this.totalWidth += config.width;
6400 this.totalWidth += 100; // assume minimum of 100 per column?
6403 if(typeof(config.cls) != 'undefined'){
6404 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6407 ['xs','sm','md','lg'].map(function(size){
6409 if(typeof(config[size]) == 'undefined'){
6413 if (!config[size]) { // 0 = hidden
6414 c.cls += ' hidden-' + size;
6418 c.cls += ' col-' + size + '-' + config[size];
6428 renderBody : function()
6438 colspan : this.cm.getColumnCount()
6448 renderFooter : function()
6458 colspan : this.cm.getColumnCount()
6472 // Roo.log('ds onload');
6477 var ds = this.store;
6479 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481 if (_this.store.sortInfo) {
6483 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484 e.select('i', true).addClass(['glyphicon-arrow-up']);
6487 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488 e.select('i', true).addClass(['glyphicon-arrow-down']);
6493 var tbody = this.mainBody;
6495 if(ds.getCount() > 0){
6496 ds.data.each(function(d,rowIndex){
6497 var row = this.renderRow(cm, ds, rowIndex);
6499 tbody.createChild(row);
6503 if(row.cellObjects.length){
6504 Roo.each(row.cellObjects, function(r){
6505 _this.renderCellObject(r);
6512 Roo.each(this.el.select('tbody td', true).elements, function(e){
6513 e.on('mouseover', _this.onMouseover, _this);
6516 Roo.each(this.el.select('tbody td', true).elements, function(e){
6517 e.on('mouseout', _this.onMouseout, _this);
6519 this.fireEvent('rowsrendered', this);
6520 //if(this.loadMask){
6521 // this.maskEl.hide();
6528 onUpdate : function(ds,record)
6530 this.refreshRow(record);
6534 onRemove : function(ds, record, index, isUpdate){
6535 if(isUpdate !== true){
6536 this.fireEvent("beforerowremoved", this, index, record);
6538 var bt = this.mainBody.dom;
6540 var rows = this.el.select('tbody > tr', true).elements;
6542 if(typeof(rows[index]) != 'undefined'){
6543 bt.removeChild(rows[index].dom);
6546 // if(bt.rows[index]){
6547 // bt.removeChild(bt.rows[index]);
6550 if(isUpdate !== true){
6551 //this.stripeRows(index);
6552 //this.syncRowHeights(index, index);
6554 this.fireEvent("rowremoved", this, index, record);
6558 onAdd : function(ds, records, rowIndex)
6560 //Roo.log('on Add called');
6561 // - note this does not handle multiple adding very well..
6562 var bt = this.mainBody.dom;
6563 for (var i =0 ; i < records.length;i++) {
6564 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565 //Roo.log(records[i]);
6566 //Roo.log(this.store.getAt(rowIndex+i));
6567 this.insertRow(this.store, rowIndex + i, false);
6574 refreshRow : function(record){
6575 var ds = this.store, index;
6576 if(typeof record == 'number'){
6578 record = ds.getAt(index);
6580 index = ds.indexOf(record);
6582 this.insertRow(ds, index, true);
6584 this.onRemove(ds, record, index+1, true);
6586 //this.syncRowHeights(index, index);
6588 this.fireEvent("rowupdated", this, index, record);
6591 insertRow : function(dm, rowIndex, isUpdate){
6594 this.fireEvent("beforerowsinserted", this, rowIndex);
6596 //var s = this.getScrollState();
6597 var row = this.renderRow(this.cm, this.store, rowIndex);
6598 // insert before rowIndex..
6599 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6603 if(row.cellObjects.length){
6604 Roo.each(row.cellObjects, function(r){
6605 _this.renderCellObject(r);
6610 this.fireEvent("rowsinserted", this, rowIndex);
6611 //this.syncRowHeights(firstRow, lastRow);
6612 //this.stripeRows(firstRow);
6619 getRowDom : function(rowIndex)
6621 var rows = this.el.select('tbody > tr', true).elements;
6623 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6626 // returns the object tree for a tr..
6629 renderRow : function(cm, ds, rowIndex)
6632 var d = ds.getAt(rowIndex);
6639 var cellObjects = [];
6641 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642 var config = cm.config[i];
6644 var renderer = cm.getRenderer(i);
6648 if(typeof(renderer) !== 'undefined'){
6649 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6651 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652 // and are rendered into the cells after the row is rendered - using the id for the element.
6654 if(typeof(value) === 'object'){
6664 rowIndex : rowIndex,
6669 this.fireEvent('rowclass', this, rowcfg);
6673 cls : rowcfg.rowClass,
6675 html: (typeof(value) === 'object') ? '' : value
6682 if(typeof(config.colspan) != 'undefined'){
6683 td.colspan = config.colspan;
6686 if(typeof(config.hidden) != 'undefined' && config.hidden){
6687 td.style += ' display:none;';
6690 if(typeof(config.align) != 'undefined' && config.align.length){
6691 td.style += ' text-align:' + config.align + ';';
6694 if(typeof(config.width) != 'undefined'){
6695 td.style += ' width:' + config.width + 'px;';
6698 if(typeof(config.cursor) != 'undefined'){
6699 td.style += ' cursor:' + config.cursor + ';';
6702 if(typeof(config.cls) != 'undefined'){
6703 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6706 ['xs','sm','md','lg'].map(function(size){
6708 if(typeof(config[size]) == 'undefined'){
6712 if (!config[size]) { // 0 = hidden
6713 td.cls += ' hidden-' + size;
6717 td.cls += ' col-' + size + '-' + config[size];
6725 row.cellObjects = cellObjects;
6733 onBeforeLoad : function()
6735 //Roo.log('ds onBeforeLoad');
6739 //if(this.loadMask){
6740 // this.maskEl.show();
6748 this.el.select('tbody', true).first().dom.innerHTML = '';
6751 * Show or hide a row.
6752 * @param {Number} rowIndex to show or hide
6753 * @param {Boolean} state hide
6755 setRowVisibility : function(rowIndex, state)
6757 var bt = this.mainBody.dom;
6759 var rows = this.el.select('tbody > tr', true).elements;
6761 if(typeof(rows[rowIndex]) == 'undefined'){
6764 rows[rowIndex].dom.style.display = state ? '' : 'none';
6768 getSelectionModel : function(){
6770 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6772 return this.selModel;
6775 * Render the Roo.bootstrap object from renderder
6777 renderCellObject : function(r)
6781 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6783 var t = r.cfg.render(r.container);
6786 Roo.each(r.cfg.cn, function(c){
6788 container: t.getChildContainer(),
6791 _this.renderCellObject(child);
6796 getRowIndex : function(row)
6800 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6811 * Returns the grid's underlying element = used by panel.Grid
6812 * @return {Element} The element
6814 getGridEl : function(){
6818 * Forces a resize - used by panel.Grid
6819 * @return {Element} The element
6821 autoSize : function()
6823 //var ctr = Roo.get(this.container.dom.parentElement);
6824 var ctr = Roo.get(this.el.dom);
6826 var thd = this.getGridEl().select('thead',true).first();
6827 var tbd = this.getGridEl().select('tbody', true).first();
6828 var tfd = this.getGridEl().select('tfoot', true).first();
6830 var cw = ctr.getWidth();
6834 tbd.setSize(ctr.getWidth(),
6835 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6837 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6840 cw = Math.max(cw, this.totalWidth);
6841 this.getGridEl().select('tr',true).setWidth(cw);
6842 // resize 'expandable coloumn?
6844 return; // we doe not have a view in this design..
6847 onBodyScroll: function()
6849 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850 this.mainHead.setStyle({
6851 'position' : 'relative',
6852 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6857 var scrollHeight = this.mainBody.dom.scrollHeight;
6859 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6861 var height = this.mainBody.getHeight();
6863 if(scrollHeight - height == scrollTop) {
6865 var total = this.ds.getTotalCount();
6867 if(this.footer.cursor + this.footer.pageSize < total){
6869 this.footer.ds.load({
6871 start : this.footer.cursor + this.footer.pageSize,
6872 limit : this.footer.pageSize
6893 * @class Roo.bootstrap.TableCell
6894 * @extends Roo.bootstrap.Component
6895 * Bootstrap TableCell class
6896 * @cfg {String} html cell contain text
6897 * @cfg {String} cls cell class
6898 * @cfg {String} tag cell tag (td|th) default td
6899 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900 * @cfg {String} align Aligns the content in a cell
6901 * @cfg {String} axis Categorizes cells
6902 * @cfg {String} bgcolor Specifies the background color of a cell
6903 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904 * @cfg {Number} colspan Specifies the number of columns a cell should span
6905 * @cfg {String} headers Specifies one or more header cells a cell is related to
6906 * @cfg {Number} height Sets the height of a cell
6907 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908 * @cfg {Number} rowspan Sets the number of rows a cell should span
6909 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910 * @cfg {String} valign Vertical aligns the content in a cell
6911 * @cfg {Number} width Specifies the width of a cell
6914 * Create a new TableCell
6915 * @param {Object} config The config object
6918 Roo.bootstrap.TableCell = function(config){
6919 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6942 getAutoCreate : function(){
6943 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6963 cfg.align=this.align
6969 cfg.bgcolor=this.bgcolor
6972 cfg.charoff=this.charoff
6975 cfg.colspan=this.colspan
6978 cfg.headers=this.headers
6981 cfg.height=this.height
6984 cfg.nowrap=this.nowrap
6987 cfg.rowspan=this.rowspan
6990 cfg.scope=this.scope
6993 cfg.valign=this.valign
6996 cfg.width=this.width
7015 * @class Roo.bootstrap.TableRow
7016 * @extends Roo.bootstrap.Component
7017 * Bootstrap TableRow class
7018 * @cfg {String} cls row class
7019 * @cfg {String} align Aligns the content in a table row
7020 * @cfg {String} bgcolor Specifies a background color for a table row
7021 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022 * @cfg {String} valign Vertical aligns the content in a table row
7025 * Create a new TableRow
7026 * @param {Object} config The config object
7029 Roo.bootstrap.TableRow = function(config){
7030 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7041 getAutoCreate : function(){
7042 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7052 cfg.align = this.align;
7055 cfg.bgcolor = this.bgcolor;
7058 cfg.charoff = this.charoff;
7061 cfg.valign = this.valign;
7079 * @class Roo.bootstrap.TableBody
7080 * @extends Roo.bootstrap.Component
7081 * Bootstrap TableBody class
7082 * @cfg {String} cls element class
7083 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084 * @cfg {String} align Aligns the content inside the element
7085 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7089 * Create a new TableBody
7090 * @param {Object} config The config object
7093 Roo.bootstrap.TableBody = function(config){
7094 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7105 getAutoCreate : function(){
7106 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7120 cfg.align = this.align;
7123 cfg.charoff = this.charoff;
7126 cfg.valign = this.valign;
7133 // initEvents : function()
7140 // this.store = Roo.factory(this.store, Roo.data);
7141 // this.store.on('load', this.onLoad, this);
7143 // this.store.load();
7147 // onLoad: function ()
7149 // this.fireEvent('load', this);
7159 * Ext JS Library 1.1.1
7160 * Copyright(c) 2006-2007, Ext JS, LLC.
7162 * Originally Released Under LGPL - original licence link has changed is not relivant.
7165 * <script type="text/javascript">
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7171 * @class Roo.form.Action
7172 * Internal Class used to handle form actions
7174 * @param {Roo.form.BasicForm} el The form element or its id
7175 * @param {Object} config Configuration options
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7183 this.options = options || {};
7186 * Client Validation Failed
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7191 * Server Validation Failed
7194 Roo.form.Action.SERVER_INVALID = 'server';
7196 * Connect to Server Failed
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7201 * Reading Data from Server Failed
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7206 Roo.form.Action.prototype = {
7208 failureType : undefined,
7209 response : undefined,
7213 run : function(options){
7218 success : function(response){
7223 handleResponse : function(response){
7227 // default connection failure
7228 failure : function(response){
7230 this.response = response;
7231 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232 this.form.afterAction(this, false);
7235 processResponse : function(response){
7236 this.response = response;
7237 if(!response.responseText){
7240 this.result = this.handleResponse(response);
7244 // utility functions used internally
7245 getUrl : function(appendParams){
7246 var url = this.options.url || this.form.url || this.form.el.dom.action;
7248 var p = this.getParams();
7250 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7256 getMethod : function(){
7257 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7260 getParams : function(){
7261 var bp = this.form.baseParams;
7262 var p = this.options.params;
7264 if(typeof p == "object"){
7265 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266 }else if(typeof p == 'string' && bp){
7267 p += '&' + Roo.urlEncode(bp);
7270 p = Roo.urlEncode(bp);
7275 createCallback : function(){
7277 success: this.success,
7278 failure: this.failure,
7280 timeout: (this.form.timeout*1000),
7281 upload: this.form.fileUpload ? this.success : undefined
7286 Roo.form.Action.Submit = function(form, options){
7287 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7293 haveProgress : false,
7294 uploadComplete : false,
7296 // uploadProgress indicator.
7297 uploadProgress : function()
7299 if (!this.form.progressUrl) {
7303 if (!this.haveProgress) {
7304 Roo.MessageBox.progress("Uploading", "Uploading");
7306 if (this.uploadComplete) {
7307 Roo.MessageBox.hide();
7311 this.haveProgress = true;
7313 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7315 var c = new Roo.data.Connection();
7317 url : this.form.progressUrl,
7322 success : function(req){
7323 //console.log(data);
7327 rdata = Roo.decode(req.responseText)
7329 Roo.log("Invalid data from server..");
7333 if (!rdata || !rdata.success) {
7335 Roo.MessageBox.alert(Roo.encode(rdata));
7338 var data = rdata.data;
7340 if (this.uploadComplete) {
7341 Roo.MessageBox.hide();
7346 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7350 this.uploadProgress.defer(2000,this);
7353 failure: function(data) {
7354 Roo.log('progress url failed ');
7365 // run get Values on the form, so it syncs any secondary forms.
7366 this.form.getValues();
7368 var o = this.options;
7369 var method = this.getMethod();
7370 var isPost = method == 'POST';
7371 if(o.clientValidation === false || this.form.isValid()){
7373 if (this.form.progressUrl) {
7374 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375 (new Date() * 1) + '' + Math.random());
7380 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381 form:this.form.el.dom,
7382 url:this.getUrl(!isPost),
7384 params:isPost ? this.getParams() : null,
7385 isUpload: this.form.fileUpload
7388 this.uploadProgress();
7390 }else if (o.clientValidation !== false){ // client validation failed
7391 this.failureType = Roo.form.Action.CLIENT_INVALID;
7392 this.form.afterAction(this, false);
7396 success : function(response)
7398 this.uploadComplete= true;
7399 if (this.haveProgress) {
7400 Roo.MessageBox.hide();
7404 var result = this.processResponse(response);
7405 if(result === true || result.success){
7406 this.form.afterAction(this, true);
7410 this.form.markInvalid(result.errors);
7411 this.failureType = Roo.form.Action.SERVER_INVALID;
7413 this.form.afterAction(this, false);
7415 failure : function(response)
7417 this.uploadComplete= true;
7418 if (this.haveProgress) {
7419 Roo.MessageBox.hide();
7422 this.response = response;
7423 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424 this.form.afterAction(this, false);
7427 handleResponse : function(response){
7428 if(this.form.errorReader){
7429 var rs = this.form.errorReader.read(response);
7432 for(var i = 0, len = rs.records.length; i < len; i++) {
7433 var r = rs.records[i];
7437 if(errors.length < 1){
7441 success : rs.success,
7447 ret = Roo.decode(response.responseText);
7451 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7461 Roo.form.Action.Load = function(form, options){
7462 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463 this.reader = this.form.reader;
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7471 Roo.Ajax.request(Roo.apply(
7472 this.createCallback(), {
7473 method:this.getMethod(),
7474 url:this.getUrl(false),
7475 params:this.getParams()
7479 success : function(response){
7481 var result = this.processResponse(response);
7482 if(result === true || !result.success || !result.data){
7483 this.failureType = Roo.form.Action.LOAD_FAILURE;
7484 this.form.afterAction(this, false);
7487 this.form.clearInvalid();
7488 this.form.setValues(result.data);
7489 this.form.afterAction(this, true);
7492 handleResponse : function(response){
7493 if(this.form.reader){
7494 var rs = this.form.reader.read(response);
7495 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7497 success : rs.success,
7501 return Roo.decode(response.responseText);
7505 Roo.form.Action.ACTION_TYPES = {
7506 'load' : Roo.form.Action.Load,
7507 'submit' : Roo.form.Action.Submit
7516 * @class Roo.bootstrap.Form
7517 * @extends Roo.bootstrap.Component
7518 * Bootstrap Form class
7519 * @cfg {String} method GET | POST (default POST)
7520 * @cfg {String} labelAlign top | left (default top)
7521 * @cfg {String} align left | right - for navbars
7522 * @cfg {Boolean} loadMask load mask when submit (default true)
7527 * @param {Object} config The config object
7531 Roo.bootstrap.Form = function(config){
7532 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7534 Roo.bootstrap.Form.popover.apply();
7538 * @event clientvalidation
7539 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540 * @param {Form} this
7541 * @param {Boolean} valid true if the form has passed client-side validation
7543 clientvalidation: true,
7545 * @event beforeaction
7546 * Fires before any action is performed. Return false to cancel the action.
7547 * @param {Form} this
7548 * @param {Action} action The action to be performed
7552 * @event actionfailed
7553 * Fires when an action fails.
7554 * @param {Form} this
7555 * @param {Action} action The action that failed
7557 actionfailed : true,
7559 * @event actioncomplete
7560 * Fires when an action is completed.
7561 * @param {Form} this
7562 * @param {Action} action The action that completed
7564 actioncomplete : true
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7572 * @cfg {String} method
7573 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7578 * The URL to use for form actions if one isn't supplied in the action options.
7581 * @cfg {Boolean} fileUpload
7582 * Set to true if this form is a file upload.
7586 * @cfg {Object} baseParams
7587 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7591 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7595 * @cfg {Sting} align (left|right) for navbar forms
7600 activeAction : null,
7603 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604 * element by passing it or its id or mask the form itself by passing in true.
7607 waitMsgTarget : false,
7612 * @cfg {Boolean} errorMask (true|false) default false
7617 * @cfg {Number} maskOffset Default 100
7621 getAutoCreate : function(){
7625 method : this.method || 'POST',
7626 id : this.id || Roo.id(),
7629 if (this.parent().xtype.match(/^Nav/)) {
7630 cfg.cls = 'navbar-form navbar-' + this.align;
7634 if (this.labelAlign == 'left' ) {
7635 cfg.cls += ' form-horizontal';
7641 initEvents : function()
7643 this.el.on('submit', this.onSubmit, this);
7644 // this was added as random key presses on the form where triggering form submit.
7645 this.el.on('keypress', function(e) {
7646 if (e.getCharCode() != 13) {
7649 // we might need to allow it for textareas.. and some other items.
7650 // check e.getTarget().
7652 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7656 Roo.log("keypress blocked");
7664 onSubmit : function(e){
7669 * Returns true if client-side validation on the form is successful.
7672 isValid : function(){
7673 var items = this.getItems();
7677 items.each(function(f){
7684 if(!target && f.el.isVisible(true)){
7690 if(this.errorMask && !valid){
7691 Roo.bootstrap.Form.popover.mask(this, target);
7698 * Returns true if any fields in this form have changed since their original load.
7701 isDirty : function(){
7703 var items = this.getItems();
7704 items.each(function(f){
7714 * Performs a predefined action (submit or load) or custom actions you define on this form.
7715 * @param {String} actionName The name of the action type
7716 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7717 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7718 * accept other config options):
7720 Property Type Description
7721 ---------------- --------------- ----------------------------------------------------------------------------------
7722 url String The url for the action (defaults to the form's url)
7723 method String The form method to use (defaults to the form's method, or POST if not defined)
7724 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7725 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7726 validate the form on the client (defaults to false)
7728 * @return {BasicForm} this
7730 doAction : function(action, options){
7731 if(typeof action == 'string'){
7732 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7734 if(this.fireEvent('beforeaction', this, action) !== false){
7735 this.beforeAction(action);
7736 action.run.defer(100, action);
7742 beforeAction : function(action){
7743 var o = action.options;
7746 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7748 // not really supported yet.. ??
7750 //if(this.waitMsgTarget === true){
7751 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7752 //}else if(this.waitMsgTarget){
7753 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7754 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7756 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7762 afterAction : function(action, success){
7763 this.activeAction = null;
7764 var o = action.options;
7766 //if(this.waitMsgTarget === true){
7768 //}else if(this.waitMsgTarget){
7769 // this.waitMsgTarget.unmask();
7771 // Roo.MessageBox.updateProgress(1);
7772 // Roo.MessageBox.hide();
7779 Roo.callback(o.success, o.scope, [this, action]);
7780 this.fireEvent('actioncomplete', this, action);
7784 // failure condition..
7785 // we have a scenario where updates need confirming.
7786 // eg. if a locking scenario exists..
7787 // we look for { errors : { needs_confirm : true }} in the response.
7789 (typeof(action.result) != 'undefined') &&
7790 (typeof(action.result.errors) != 'undefined') &&
7791 (typeof(action.result.errors.needs_confirm) != 'undefined')
7794 Roo.log("not supported yet");
7797 Roo.MessageBox.confirm(
7798 "Change requires confirmation",
7799 action.result.errorMsg,
7804 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7814 Roo.callback(o.failure, o.scope, [this, action]);
7815 // show an error message if no failed handler is set..
7816 if (!this.hasListener('actionfailed')) {
7817 Roo.log("need to add dialog support");
7819 Roo.MessageBox.alert("Error",
7820 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7821 action.result.errorMsg :
7822 "Saving Failed, please check your entries or try again"
7827 this.fireEvent('actionfailed', this, action);
7832 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7833 * @param {String} id The value to search for
7836 findField : function(id){
7837 var items = this.getItems();
7838 var field = items.get(id);
7840 items.each(function(f){
7841 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7848 return field || null;
7851 * Mark fields in this form invalid in bulk.
7852 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7853 * @return {BasicForm} this
7855 markInvalid : function(errors){
7856 if(errors instanceof Array){
7857 for(var i = 0, len = errors.length; i < len; i++){
7858 var fieldError = errors[i];
7859 var f = this.findField(fieldError.id);
7861 f.markInvalid(fieldError.msg);
7867 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7868 field.markInvalid(errors[id]);
7872 //Roo.each(this.childForms || [], function (f) {
7873 // f.markInvalid(errors);
7880 * Set values for fields in this form in bulk.
7881 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7882 * @return {BasicForm} this
7884 setValues : function(values){
7885 if(values instanceof Array){ // array of objects
7886 for(var i = 0, len = values.length; i < len; i++){
7888 var f = this.findField(v.id);
7890 f.setValue(v.value);
7891 if(this.trackResetOnLoad){
7892 f.originalValue = f.getValue();
7896 }else{ // object hash
7899 if(typeof values[id] != 'function' && (field = this.findField(id))){
7901 if (field.setFromData &&
7903 field.displayField &&
7904 // combos' with local stores can
7905 // be queried via setValue()
7906 // to set their value..
7907 (field.store && !field.store.isLocal)
7911 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7912 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7913 field.setFromData(sd);
7916 field.setValue(values[id]);
7920 if(this.trackResetOnLoad){
7921 field.originalValue = field.getValue();
7927 //Roo.each(this.childForms || [], function (f) {
7928 // f.setValues(values);
7935 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7936 * they are returned as an array.
7937 * @param {Boolean} asString
7940 getValues : function(asString){
7941 //if (this.childForms) {
7942 // copy values from the child forms
7943 // Roo.each(this.childForms, function (f) {
7944 // this.setValues(f.getValues());
7950 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7951 if(asString === true){
7954 return Roo.urlDecode(fs);
7958 * Returns the fields in this form as an object with key/value pairs.
7959 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7962 getFieldValues : function(with_hidden)
7964 var items = this.getItems();
7966 items.each(function(f){
7970 var v = f.getValue();
7971 if (f.inputType =='radio') {
7972 if (typeof(ret[f.getName()]) == 'undefined') {
7973 ret[f.getName()] = ''; // empty..
7976 if (!f.el.dom.checked) {
7984 // not sure if this supported any more..
7985 if ((typeof(v) == 'object') && f.getRawValue) {
7986 v = f.getRawValue() ; // dates..
7988 // combo boxes where name != hiddenName...
7989 if (f.name !== false && f.name != '' && f.name != f.getName()) {
7990 ret[f.name] = f.getRawValue();
7992 ret[f.getName()] = v;
7999 * Clears all invalid messages in this form.
8000 * @return {BasicForm} this
8002 clearInvalid : function(){
8003 var items = this.getItems();
8005 items.each(function(f){
8016 * @return {BasicForm} this
8019 var items = this.getItems();
8020 items.each(function(f){
8024 Roo.each(this.childForms || [], function (f) {
8031 getItems : function()
8033 var r=new Roo.util.MixedCollection(false, function(o){
8034 return o.id || (o.id = Roo.id());
8036 var iter = function(el) {
8043 Roo.each(el.items,function(e) {
8060 Roo.apply(Roo.bootstrap.Form, {
8087 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8088 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8089 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8090 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8093 this.maskEl.top.enableDisplayMode("block");
8094 this.maskEl.left.enableDisplayMode("block");
8095 this.maskEl.bottom.enableDisplayMode("block");
8096 this.maskEl.right.enableDisplayMode("block");
8098 this.toolTip = new Roo.bootstrap.Tooltip({
8099 cls : 'roo-form-error-popover',
8101 'left' : ['r-l', [-2,0], 'right'],
8102 'right' : ['l-r', [2,0], 'left'],
8103 'bottom' : ['tl-bl', [0,2], 'top'],
8104 'top' : [ 'bl-tl', [0,-2], 'bottom']
8108 this.toolTip.render(Roo.get(document.body));
8110 this.toolTip.el.enableDisplayMode("block");
8112 Roo.get(document.body).on('click', function(){
8116 Roo.get(document.body).on('touchstart', function(){
8120 this.isApplied = true
8123 mask : function(form, target)
8127 this.target = target;
8129 if(!this.form.errorMask || !target.el){
8133 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8135 Roo.log(scrollable);
8137 var ot = this.target.el.calcOffsetsTo(scrollable);
8139 var scrollTo = ot[1] - this.form.maskOffset;
8141 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8143 scrollable.scrollTo('top', scrollTo);
8145 var box = this.target.el.getBox();
8147 var zIndex = Roo.bootstrap.Modal.zIndex++;
8150 this.maskEl.top.setStyle('position', 'absolute');
8151 this.maskEl.top.setStyle('z-index', zIndex);
8152 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8153 this.maskEl.top.setLeft(0);
8154 this.maskEl.top.setTop(0);
8155 this.maskEl.top.show();
8157 this.maskEl.left.setStyle('position', 'absolute');
8158 this.maskEl.left.setStyle('z-index', zIndex);
8159 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8160 this.maskEl.left.setLeft(0);
8161 this.maskEl.left.setTop(box.y - this.padding);
8162 this.maskEl.left.show();
8164 this.maskEl.bottom.setStyle('position', 'absolute');
8165 this.maskEl.bottom.setStyle('z-index', zIndex);
8166 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8167 this.maskEl.bottom.setLeft(0);
8168 this.maskEl.bottom.setTop(box.bottom + this.padding);
8169 this.maskEl.bottom.show();
8171 this.maskEl.right.setStyle('position', 'absolute');
8172 this.maskEl.right.setStyle('z-index', zIndex);
8173 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8174 this.maskEl.right.setLeft(box.right + this.padding);
8175 this.maskEl.right.setTop(box.y - this.padding);
8176 this.maskEl.right.show();
8178 this.toolTip.bindEl = this.target.el;
8180 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8182 var tip = this.target.blankText;
8184 if(this.target.getValue() !== '' ) {
8186 if (this.target.invalidText.length) {
8187 tip = this.target.invalidText;
8188 } else if (this.target.regexText.length){
8189 tip = this.target.regexText;
8193 this.toolTip.show(tip);
8195 this.intervalID = window.setInterval(function() {
8196 Roo.bootstrap.Form.popover.unmask();
8199 window.onwheel = function(){ return false;};
8201 (function(){ this.isMasked = true; }).defer(500, this);
8207 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8211 this.maskEl.top.setStyle('position', 'absolute');
8212 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8213 this.maskEl.top.hide();
8215 this.maskEl.left.setStyle('position', 'absolute');
8216 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8217 this.maskEl.left.hide();
8219 this.maskEl.bottom.setStyle('position', 'absolute');
8220 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8221 this.maskEl.bottom.hide();
8223 this.maskEl.right.setStyle('position', 'absolute');
8224 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8225 this.maskEl.right.hide();
8227 this.toolTip.hide();
8229 this.toolTip.el.hide();
8231 window.onwheel = function(){ return true;};
8233 if(this.intervalID){
8234 window.clearInterval(this.intervalID);
8235 this.intervalID = false;
8238 this.isMasked = false;
8248 * Ext JS Library 1.1.1
8249 * Copyright(c) 2006-2007, Ext JS, LLC.
8251 * Originally Released Under LGPL - original licence link has changed is not relivant.
8254 * <script type="text/javascript">
8257 * @class Roo.form.VTypes
8258 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8261 Roo.form.VTypes = function(){
8262 // closure these in so they are only created once.
8263 var alpha = /^[a-zA-Z_]+$/;
8264 var alphanum = /^[a-zA-Z0-9_]+$/;
8265 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8266 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8268 // All these messages and functions are configurable
8271 * The function used to validate email addresses
8272 * @param {String} value The email address
8274 'email' : function(v){
8275 return email.test(v);
8278 * The error text to display when the email validation function returns false
8281 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8283 * The keystroke filter mask to be applied on email input
8286 'emailMask' : /[a-z0-9_\.\-@]/i,
8289 * The function used to validate URLs
8290 * @param {String} value The URL
8292 'url' : function(v){
8296 * The error text to display when the url validation function returns false
8299 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8302 * The function used to validate alpha values
8303 * @param {String} value The value
8305 'alpha' : function(v){
8306 return alpha.test(v);
8309 * The error text to display when the alpha validation function returns false
8312 'alphaText' : 'This field should only contain letters and _',
8314 * The keystroke filter mask to be applied on alpha input
8317 'alphaMask' : /[a-z_]/i,
8320 * The function used to validate alphanumeric values
8321 * @param {String} value The value
8323 'alphanum' : function(v){
8324 return alphanum.test(v);
8327 * The error text to display when the alphanumeric validation function returns false
8330 'alphanumText' : 'This field should only contain letters, numbers and _',
8332 * The keystroke filter mask to be applied on alphanumeric input
8335 'alphanumMask' : /[a-z0-9_]/i
8345 * @class Roo.bootstrap.Input
8346 * @extends Roo.bootstrap.Component
8347 * Bootstrap Input class
8348 * @cfg {Boolean} disabled is it disabled
8349 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8350 * @cfg {String} name name of the input
8351 * @cfg {string} fieldLabel - the label associated
8352 * @cfg {string} placeholder - placeholder to put in text.
8353 * @cfg {string} before - input group add on before
8354 * @cfg {string} after - input group add on after
8355 * @cfg {string} size - (lg|sm) or leave empty..
8356 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8357 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8358 * @cfg {Number} md colspan out of 12 for computer-sized screens
8359 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8360 * @cfg {string} value default value of the input
8361 * @cfg {Number} labelWidth set the width of label
8362 * @cfg {Number} labellg set the width of label (1-12)
8363 * @cfg {Number} labelmd set the width of label (1-12)
8364 * @cfg {Number} labelsm set the width of label (1-12)
8365 * @cfg {Number} labelxs set the width of label (1-12)
8366 * @cfg {String} labelAlign (top|left)
8367 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8368 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8369 * @cfg {String} indicatorpos (left|right) default left
8371 * @cfg {String} align (left|center|right) Default left
8372 * @cfg {Boolean} forceFeedback (true|false) Default false
8378 * Create a new Input
8379 * @param {Object} config The config object
8382 Roo.bootstrap.Input = function(config){
8384 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8389 * Fires when this field receives input focus.
8390 * @param {Roo.form.Field} this
8395 * Fires when this field loses input focus.
8396 * @param {Roo.form.Field} this
8401 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8402 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8403 * @param {Roo.form.Field} this
8404 * @param {Roo.EventObject} e The event object
8409 * Fires just before the field blurs if the field value has changed.
8410 * @param {Roo.form.Field} this
8411 * @param {Mixed} newValue The new value
8412 * @param {Mixed} oldValue The original value
8417 * Fires after the field has been marked as invalid.
8418 * @param {Roo.form.Field} this
8419 * @param {String} msg The validation message
8424 * Fires after the field has been validated with no errors.
8425 * @param {Roo.form.Field} this
8430 * Fires after the key up
8431 * @param {Roo.form.Field} this
8432 * @param {Roo.EventObject} e The event Object
8438 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8440 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8441 automatic validation (defaults to "keyup").
8443 validationEvent : "keyup",
8445 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8447 validateOnBlur : true,
8449 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8451 validationDelay : 250,
8453 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8455 focusClass : "x-form-focus", // not needed???
8459 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8461 invalidClass : "has-warning",
8464 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8466 validClass : "has-success",
8469 * @cfg {Boolean} hasFeedback (true|false) default true
8474 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8476 invalidFeedbackClass : "glyphicon-warning-sign",
8479 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8481 validFeedbackClass : "glyphicon-ok",
8484 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8486 selectOnFocus : false,
8489 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8493 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8498 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8500 disableKeyFilter : false,
8503 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8507 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8511 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8513 blankText : "Please complete this mandatory field",
8516 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8520 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8522 maxLength : Number.MAX_VALUE,
8524 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8526 minLengthText : "The minimum length for this field is {0}",
8528 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8530 maxLengthText : "The maximum length for this field is {0}",
8534 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8535 * If available, this function will be called only after the basic validators all return true, and will be passed the
8536 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8540 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8541 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8542 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8546 * @cfg {String} regexText -- Depricated - use Invalid Text
8551 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8557 autocomplete: false,
8576 formatedValue : false,
8577 forceFeedback : false,
8579 indicatorpos : 'left',
8586 parentLabelAlign : function()
8589 while (parent.parent()) {
8590 parent = parent.parent();
8591 if (typeof(parent.labelAlign) !='undefined') {
8592 return parent.labelAlign;
8599 getAutoCreate : function()
8601 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8607 if(this.inputType != 'hidden'){
8608 cfg.cls = 'form-group' //input-group
8614 type : this.inputType,
8616 cls : 'form-control',
8617 placeholder : this.placeholder || '',
8618 autocomplete : this.autocomplete || 'new-password'
8622 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8625 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8626 input.maxLength = this.maxLength;
8629 if (this.disabled) {
8630 input.disabled=true;
8633 if (this.readOnly) {
8634 input.readonly=true;
8638 input.name = this.name;
8642 input.cls += ' input-' + this.size;
8646 ['xs','sm','md','lg'].map(function(size){
8647 if (settings[size]) {
8648 cfg.cls += ' col-' + size + '-' + settings[size];
8652 var inputblock = input;
8656 cls: 'glyphicon form-control-feedback'
8659 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8662 cls : 'has-feedback',
8670 if (this.before || this.after) {
8673 cls : 'input-group',
8677 if (this.before && typeof(this.before) == 'string') {
8679 inputblock.cn.push({
8681 cls : 'roo-input-before input-group-addon',
8685 if (this.before && typeof(this.before) == 'object') {
8686 this.before = Roo.factory(this.before);
8688 inputblock.cn.push({
8690 cls : 'roo-input-before input-group-' +
8691 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8695 inputblock.cn.push(input);
8697 if (this.after && typeof(this.after) == 'string') {
8698 inputblock.cn.push({
8700 cls : 'roo-input-after input-group-addon',
8704 if (this.after && typeof(this.after) == 'object') {
8705 this.after = Roo.factory(this.after);
8707 inputblock.cn.push({
8709 cls : 'roo-input-after input-group-' +
8710 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8714 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8715 inputblock.cls += ' has-feedback';
8716 inputblock.cn.push(feedback);
8720 if (align ==='left' && this.fieldLabel.length) {
8722 cfg.cls += ' roo-form-group-label-left';
8727 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8728 tooltip : 'This field is required'
8733 cls : 'control-label',
8734 html : this.fieldLabel
8745 var labelCfg = cfg.cn[1];
8746 var contentCfg = cfg.cn[2];
8748 if(this.indicatorpos == 'right'){
8753 cls : 'control-label',
8757 html : this.fieldLabel
8761 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8762 tooltip : 'This field is required'
8775 labelCfg = cfg.cn[0];
8776 contentCfg = cfg.cn[1];
8780 if(this.labelWidth > 12){
8781 labelCfg.style = "width: " + this.labelWidth + 'px';
8784 if(this.labelWidth < 13 && this.labelmd == 0){
8785 this.labelmd = this.labelWidth;
8788 if(this.labellg > 0){
8789 labelCfg.cls += ' col-lg-' + this.labellg;
8790 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8793 if(this.labelmd > 0){
8794 labelCfg.cls += ' col-md-' + this.labelmd;
8795 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8798 if(this.labelsm > 0){
8799 labelCfg.cls += ' col-sm-' + this.labelsm;
8800 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8803 if(this.labelxs > 0){
8804 labelCfg.cls += ' col-xs-' + this.labelxs;
8805 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8809 } else if ( this.fieldLabel.length) {
8814 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8815 tooltip : 'This field is required'
8819 //cls : 'input-group-addon',
8820 html : this.fieldLabel
8828 if(this.indicatorpos == 'right'){
8833 //cls : 'input-group-addon',
8834 html : this.fieldLabel
8839 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8840 tooltip : 'This field is required'
8860 if (this.parentType === 'Navbar' && this.parent().bar) {
8861 cfg.cls += ' navbar-form';
8864 if (this.parentType === 'NavGroup') {
8865 cfg.cls += ' navbar-form';
8873 * return the real input element.
8875 inputEl: function ()
8877 return this.el.select('input.form-control',true).first();
8880 tooltipEl : function()
8882 return this.inputEl();
8885 indicatorEl : function()
8887 var indicator = this.el.select('i.roo-required-indicator',true).first();
8897 setDisabled : function(v)
8899 var i = this.inputEl().dom;
8901 i.removeAttribute('disabled');
8905 i.setAttribute('disabled','true');
8907 initEvents : function()
8910 this.inputEl().on("keydown" , this.fireKey, this);
8911 this.inputEl().on("focus", this.onFocus, this);
8912 this.inputEl().on("blur", this.onBlur, this);
8914 this.inputEl().relayEvent('keyup', this);
8916 this.indicator = this.indicatorEl();
8919 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8920 this.indicator.hide();
8923 // reference to original value for reset
8924 this.originalValue = this.getValue();
8925 //Roo.form.TextField.superclass.initEvents.call(this);
8926 if(this.validationEvent == 'keyup'){
8927 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8928 this.inputEl().on('keyup', this.filterValidation, this);
8930 else if(this.validationEvent !== false){
8931 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8934 if(this.selectOnFocus){
8935 this.on("focus", this.preFocus, this);
8938 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8939 this.inputEl().on("keypress", this.filterKeys, this);
8941 this.inputEl().relayEvent('keypress', this);
8944 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8945 this.el.on("click", this.autoSize, this);
8948 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8949 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8952 if (typeof(this.before) == 'object') {
8953 this.before.render(this.el.select('.roo-input-before',true).first());
8955 if (typeof(this.after) == 'object') {
8956 this.after.render(this.el.select('.roo-input-after',true).first());
8961 filterValidation : function(e){
8962 if(!e.isNavKeyPress()){
8963 this.validationTask.delay(this.validationDelay);
8967 * Validates the field value
8968 * @return {Boolean} True if the value is valid, else false
8970 validate : function(){
8971 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8972 if(this.disabled || this.validateValue(this.getRawValue())){
8983 * Validates a value according to the field's validation rules and marks the field as invalid
8984 * if the validation fails
8985 * @param {Mixed} value The value to validate
8986 * @return {Boolean} True if the value is valid, else false
8988 validateValue : function(value){
8989 if(value.length < 1) { // if it's blank
8990 if(this.allowBlank){
8993 return this.inputEl().hasClass('hide') ? true : false;
8996 if(value.length < this.minLength){
8999 if(value.length > this.maxLength){
9003 var vt = Roo.form.VTypes;
9004 if(!vt[this.vtype](value, this)){
9008 if(typeof this.validator == "function"){
9009 var msg = this.validator(value);
9013 if (typeof(msg) == 'string') {
9014 this.invalidText = msg;
9018 if(this.regex && !this.regex.test(value)){
9028 fireKey : function(e){
9029 //Roo.log('field ' + e.getKey());
9030 if(e.isNavKeyPress()){
9031 this.fireEvent("specialkey", this, e);
9034 focus : function (selectText){
9036 this.inputEl().focus();
9037 if(selectText === true){
9038 this.inputEl().dom.select();
9044 onFocus : function(){
9045 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9046 // this.el.addClass(this.focusClass);
9049 this.hasFocus = true;
9050 this.startValue = this.getValue();
9051 this.fireEvent("focus", this);
9055 beforeBlur : Roo.emptyFn,
9059 onBlur : function(){
9061 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9062 //this.el.removeClass(this.focusClass);
9064 this.hasFocus = false;
9065 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9068 var v = this.getValue();
9069 if(String(v) !== String(this.startValue)){
9070 this.fireEvent('change', this, v, this.startValue);
9072 this.fireEvent("blur", this);
9076 * Resets the current field value to the originally loaded value and clears any validation messages
9079 this.setValue(this.originalValue);
9083 * Returns the name of the field
9084 * @return {Mixed} name The name field
9086 getName: function(){
9090 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9091 * @return {Mixed} value The field value
9093 getValue : function(){
9095 var v = this.inputEl().getValue();
9100 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9101 * @return {Mixed} value The field value
9103 getRawValue : function(){
9104 var v = this.inputEl().getValue();
9110 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9111 * @param {Mixed} value The value to set
9113 setRawValue : function(v){
9114 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9117 selectText : function(start, end){
9118 var v = this.getRawValue();
9120 start = start === undefined ? 0 : start;
9121 end = end === undefined ? v.length : end;
9122 var d = this.inputEl().dom;
9123 if(d.setSelectionRange){
9124 d.setSelectionRange(start, end);
9125 }else if(d.createTextRange){
9126 var range = d.createTextRange();
9127 range.moveStart("character", start);
9128 range.moveEnd("character", v.length-end);
9135 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9136 * @param {Mixed} value The value to set
9138 setValue : function(v){
9141 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9147 processValue : function(value){
9148 if(this.stripCharsRe){
9149 var newValue = value.replace(this.stripCharsRe, '');
9150 if(newValue !== value){
9151 this.setRawValue(newValue);
9158 preFocus : function(){
9160 if(this.selectOnFocus){
9161 this.inputEl().dom.select();
9164 filterKeys : function(e){
9166 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9169 var c = e.getCharCode(), cc = String.fromCharCode(c);
9170 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9173 if(!this.maskRe.test(cc)){
9178 * Clear any invalid styles/messages for this field
9180 clearInvalid : function(){
9182 if(!this.el || this.preventMark){ // not rendered
9187 this.el.removeClass(this.invalidClass);
9189 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9191 var feedback = this.el.select('.form-control-feedback', true).first();
9194 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9199 this.fireEvent('valid', this);
9203 * Mark this field as valid
9205 markValid : function()
9207 if(!this.el || this.preventMark){ // not rendered...
9211 this.el.removeClass([this.invalidClass, this.validClass]);
9213 var feedback = this.el.select('.form-control-feedback', true).first();
9216 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9223 if(this.allowBlank && !this.getRawValue().length){
9228 this.indicator.hide();
9231 this.el.addClass(this.validClass);
9233 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9235 var feedback = this.el.select('.form-control-feedback', true).first();
9238 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9239 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9244 this.fireEvent('valid', this);
9248 * Mark this field as invalid
9249 * @param {String} msg The validation message
9251 markInvalid : function(msg)
9253 if(!this.el || this.preventMark){ // not rendered
9257 this.el.removeClass([this.invalidClass, this.validClass]);
9259 var feedback = this.el.select('.form-control-feedback', true).first();
9262 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9269 if(this.allowBlank && !this.getRawValue().length){
9274 this.indicator.show();
9277 this.el.addClass(this.invalidClass);
9279 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9281 var feedback = this.el.select('.form-control-feedback', true).first();
9284 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9286 if(this.getValue().length || this.forceFeedback){
9287 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9294 this.fireEvent('invalid', this, msg);
9297 SafariOnKeyDown : function(event)
9299 // this is a workaround for a password hang bug on chrome/ webkit.
9300 if (this.inputEl().dom.type != 'password') {
9304 var isSelectAll = false;
9306 if(this.inputEl().dom.selectionEnd > 0){
9307 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9309 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9310 event.preventDefault();
9315 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9317 event.preventDefault();
9318 // this is very hacky as keydown always get's upper case.
9320 var cc = String.fromCharCode(event.getCharCode());
9321 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9325 adjustWidth : function(tag, w){
9326 tag = tag.toLowerCase();
9327 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9328 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9332 if(tag == 'textarea'){
9335 }else if(Roo.isOpera){
9339 if(tag == 'textarea'){
9347 setFieldLabel : function(v)
9349 this.fieldLabel = v;
9352 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9366 * @class Roo.bootstrap.TextArea
9367 * @extends Roo.bootstrap.Input
9368 * Bootstrap TextArea class
9369 * @cfg {Number} cols Specifies the visible width of a text area
9370 * @cfg {Number} rows Specifies the visible number of lines in a text area
9371 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9372 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9373 * @cfg {string} html text
9376 * Create a new TextArea
9377 * @param {Object} config The config object
9380 Roo.bootstrap.TextArea = function(config){
9381 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9385 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9395 getAutoCreate : function(){
9397 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9403 if(this.inputType != 'hidden'){
9404 cfg.cls = 'form-group' //input-group
9412 value : this.value || '',
9413 html: this.html || '',
9414 cls : 'form-control',
9415 placeholder : this.placeholder || ''
9419 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9420 input.maxLength = this.maxLength;
9424 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9428 input.cols = this.cols;
9431 if (this.readOnly) {
9432 input.readonly = true;
9436 input.name = this.name;
9440 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9444 ['xs','sm','md','lg'].map(function(size){
9445 if (settings[size]) {
9446 cfg.cls += ' col-' + size + '-' + settings[size];
9450 var inputblock = input;
9452 if(this.hasFeedback && !this.allowBlank){
9456 cls: 'glyphicon form-control-feedback'
9460 cls : 'has-feedback',
9469 if (this.before || this.after) {
9472 cls : 'input-group',
9476 inputblock.cn.push({
9478 cls : 'input-group-addon',
9483 inputblock.cn.push(input);
9485 if(this.hasFeedback && !this.allowBlank){
9486 inputblock.cls += ' has-feedback';
9487 inputblock.cn.push(feedback);
9491 inputblock.cn.push({
9493 cls : 'input-group-addon',
9500 if (align ==='left' && this.fieldLabel.length) {
9505 cls : 'control-label',
9506 html : this.fieldLabel
9517 if(this.labelWidth > 12){
9518 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9521 if(this.labelWidth < 13 && this.labelmd == 0){
9522 this.labelmd = this.labelWidth;
9525 if(this.labellg > 0){
9526 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9527 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9530 if(this.labelmd > 0){
9531 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9532 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9535 if(this.labelsm > 0){
9536 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9537 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9540 if(this.labelxs > 0){
9541 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9542 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9545 } else if ( this.fieldLabel.length) {
9550 //cls : 'input-group-addon',
9551 html : this.fieldLabel
9569 if (this.disabled) {
9570 input.disabled=true;
9577 * return the real textarea element.
9579 inputEl: function ()
9581 return this.el.select('textarea.form-control',true).first();
9585 * Clear any invalid styles/messages for this field
9587 clearInvalid : function()
9590 if(!this.el || this.preventMark){ // not rendered
9594 var label = this.el.select('label', true).first();
9595 var icon = this.el.select('i.fa-star', true).first();
9601 this.el.removeClass(this.invalidClass);
9603 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9605 var feedback = this.el.select('.form-control-feedback', true).first();
9608 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9613 this.fireEvent('valid', this);
9617 * Mark this field as valid
9619 markValid : function()
9621 if(!this.el || this.preventMark){ // not rendered
9625 this.el.removeClass([this.invalidClass, this.validClass]);
9627 var feedback = this.el.select('.form-control-feedback', true).first();
9630 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9633 if(this.disabled || this.allowBlank){
9637 var label = this.el.select('label', true).first();
9638 var icon = this.el.select('i.fa-star', true).first();
9644 this.el.addClass(this.validClass);
9646 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9648 var feedback = this.el.select('.form-control-feedback', true).first();
9651 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9652 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9657 this.fireEvent('valid', this);
9661 * Mark this field as invalid
9662 * @param {String} msg The validation message
9664 markInvalid : function(msg)
9666 if(!this.el || this.preventMark){ // not rendered
9670 this.el.removeClass([this.invalidClass, this.validClass]);
9672 var feedback = this.el.select('.form-control-feedback', true).first();
9675 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9678 if(this.disabled || this.allowBlank){
9682 var label = this.el.select('label', true).first();
9683 var icon = this.el.select('i.fa-star', true).first();
9685 if(!this.getValue().length && label && !icon){
9686 this.el.createChild({
9688 cls : 'text-danger fa fa-lg fa-star',
9689 tooltip : 'This field is required',
9690 style : 'margin-right:5px;'
9694 this.el.addClass(this.invalidClass);
9696 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9698 var feedback = this.el.select('.form-control-feedback', true).first();
9701 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9703 if(this.getValue().length || this.forceFeedback){
9704 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9711 this.fireEvent('invalid', this, msg);
9719 * trigger field - base class for combo..
9724 * @class Roo.bootstrap.TriggerField
9725 * @extends Roo.bootstrap.Input
9726 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9727 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9728 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9729 * for which you can provide a custom implementation. For example:
9731 var trigger = new Roo.bootstrap.TriggerField();
9732 trigger.onTriggerClick = myTriggerFn;
9733 trigger.applyTo('my-field');
9736 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9737 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9738 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9739 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9740 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9743 * Create a new TriggerField.
9744 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9745 * to the base TextField)
9747 Roo.bootstrap.TriggerField = function(config){
9748 this.mimicing = false;
9749 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9752 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9754 * @cfg {String} triggerClass A CSS class to apply to the trigger
9757 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9762 * @cfg {Boolean} removable (true|false) special filter default false
9766 /** @cfg {Boolean} grow @hide */
9767 /** @cfg {Number} growMin @hide */
9768 /** @cfg {Number} growMax @hide */
9774 autoSize: Roo.emptyFn,
9781 actionMode : 'wrap',
9786 getAutoCreate : function(){
9788 var align = this.labelAlign || this.parentLabelAlign();
9793 cls: 'form-group' //input-group
9800 type : this.inputType,
9801 cls : 'form-control',
9802 autocomplete: 'new-password',
9803 placeholder : this.placeholder || ''
9807 input.name = this.name;
9810 input.cls += ' input-' + this.size;
9813 if (this.disabled) {
9814 input.disabled=true;
9817 var inputblock = input;
9819 if(this.hasFeedback && !this.allowBlank){
9823 cls: 'glyphicon form-control-feedback'
9826 if(this.removable && !this.editable && !this.tickable){
9828 cls : 'has-feedback',
9834 cls : 'roo-combo-removable-btn close'
9841 cls : 'has-feedback',
9850 if(this.removable && !this.editable && !this.tickable){
9852 cls : 'roo-removable',
9858 cls : 'roo-combo-removable-btn close'
9865 if (this.before || this.after) {
9868 cls : 'input-group',
9872 inputblock.cn.push({
9874 cls : 'input-group-addon',
9879 inputblock.cn.push(input);
9881 if(this.hasFeedback && !this.allowBlank){
9882 inputblock.cls += ' has-feedback';
9883 inputblock.cn.push(feedback);
9887 inputblock.cn.push({
9889 cls : 'input-group-addon',
9902 cls: 'form-hidden-field'
9916 cls: 'form-hidden-field'
9920 cls: 'roo-select2-choices',
9924 cls: 'roo-select2-search-field',
9937 cls: 'roo-select2-container input-group',
9942 // cls: 'typeahead typeahead-long dropdown-menu',
9943 // style: 'display:none'
9948 if(!this.multiple && this.showToggleBtn){
9954 if (this.caret != false) {
9957 cls: 'fa fa-' + this.caret
9964 cls : 'input-group-addon btn dropdown-toggle',
9969 cls: 'combobox-clear',
9983 combobox.cls += ' roo-select2-container-multi';
9986 if (align ==='left' && this.fieldLabel.length) {
9988 cfg.cls += ' roo-form-group-label-left';
9993 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9994 tooltip : 'This field is required'
9999 cls : 'control-label',
10000 html : this.fieldLabel
10012 var labelCfg = cfg.cn[1];
10013 var contentCfg = cfg.cn[2];
10015 if(this.indicatorpos == 'right'){
10020 cls : 'control-label',
10024 html : this.fieldLabel
10028 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10029 tooltip : 'This field is required'
10042 labelCfg = cfg.cn[0];
10043 contentCfg = cfg.cn[1];
10046 if(this.labelWidth > 12){
10047 labelCfg.style = "width: " + this.labelWidth + 'px';
10050 if(this.labelWidth < 13 && this.labelmd == 0){
10051 this.labelmd = this.labelWidth;
10054 if(this.labellg > 0){
10055 labelCfg.cls += ' col-lg-' + this.labellg;
10056 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10059 if(this.labelmd > 0){
10060 labelCfg.cls += ' col-md-' + this.labelmd;
10061 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10064 if(this.labelsm > 0){
10065 labelCfg.cls += ' col-sm-' + this.labelsm;
10066 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10069 if(this.labelxs > 0){
10070 labelCfg.cls += ' col-xs-' + this.labelxs;
10071 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10074 } else if ( this.fieldLabel.length) {
10075 // Roo.log(" label");
10079 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10080 tooltip : 'This field is required'
10084 //cls : 'input-group-addon',
10085 html : this.fieldLabel
10093 if(this.indicatorpos == 'right'){
10101 html : this.fieldLabel
10105 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10106 tooltip : 'This field is required'
10119 // Roo.log(" no label && no align");
10126 ['xs','sm','md','lg'].map(function(size){
10127 if (settings[size]) {
10128 cfg.cls += ' col-' + size + '-' + settings[size];
10139 onResize : function(w, h){
10140 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10141 // if(typeof w == 'number'){
10142 // var x = w - this.trigger.getWidth();
10143 // this.inputEl().setWidth(this.adjustWidth('input', x));
10144 // this.trigger.setStyle('left', x+'px');
10149 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10152 getResizeEl : function(){
10153 return this.inputEl();
10157 getPositionEl : function(){
10158 return this.inputEl();
10162 alignErrorIcon : function(){
10163 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10167 initEvents : function(){
10171 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10172 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10173 if(!this.multiple && this.showToggleBtn){
10174 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10175 if(this.hideTrigger){
10176 this.trigger.setDisplayed(false);
10178 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10182 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10185 if(this.removable && !this.editable && !this.tickable){
10186 var close = this.closeTriggerEl();
10189 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10190 close.on('click', this.removeBtnClick, this, close);
10194 //this.trigger.addClassOnOver('x-form-trigger-over');
10195 //this.trigger.addClassOnClick('x-form-trigger-click');
10198 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10202 closeTriggerEl : function()
10204 var close = this.el.select('.roo-combo-removable-btn', true).first();
10205 return close ? close : false;
10208 removeBtnClick : function(e, h, el)
10210 e.preventDefault();
10212 if(this.fireEvent("remove", this) !== false){
10214 this.fireEvent("afterremove", this)
10218 createList : function()
10220 this.list = Roo.get(document.body).createChild({
10222 cls: 'typeahead typeahead-long dropdown-menu',
10223 style: 'display:none'
10226 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10231 initTrigger : function(){
10236 onDestroy : function(){
10238 this.trigger.removeAllListeners();
10239 // this.trigger.remove();
10242 // this.wrap.remove();
10244 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10248 onFocus : function(){
10249 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10251 if(!this.mimicing){
10252 this.wrap.addClass('x-trigger-wrap-focus');
10253 this.mimicing = true;
10254 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10255 if(this.monitorTab){
10256 this.el.on("keydown", this.checkTab, this);
10263 checkTab : function(e){
10264 if(e.getKey() == e.TAB){
10265 this.triggerBlur();
10270 onBlur : function(){
10275 mimicBlur : function(e, t){
10277 if(!this.wrap.contains(t) && this.validateBlur()){
10278 this.triggerBlur();
10284 triggerBlur : function(){
10285 this.mimicing = false;
10286 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10287 if(this.monitorTab){
10288 this.el.un("keydown", this.checkTab, this);
10290 //this.wrap.removeClass('x-trigger-wrap-focus');
10291 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10295 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10296 validateBlur : function(e, t){
10301 onDisable : function(){
10302 this.inputEl().dom.disabled = true;
10303 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10305 // this.wrap.addClass('x-item-disabled');
10310 onEnable : function(){
10311 this.inputEl().dom.disabled = false;
10312 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10314 // this.el.removeClass('x-item-disabled');
10319 onShow : function(){
10320 var ae = this.getActionEl();
10323 ae.dom.style.display = '';
10324 ae.dom.style.visibility = 'visible';
10330 onHide : function(){
10331 var ae = this.getActionEl();
10332 ae.dom.style.display = 'none';
10336 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10337 * by an implementing function.
10339 * @param {EventObject} e
10341 onTriggerClick : Roo.emptyFn
10345 * Ext JS Library 1.1.1
10346 * Copyright(c) 2006-2007, Ext JS, LLC.
10348 * Originally Released Under LGPL - original licence link has changed is not relivant.
10351 * <script type="text/javascript">
10356 * @class Roo.data.SortTypes
10358 * Defines the default sorting (casting?) comparison functions used when sorting data.
10360 Roo.data.SortTypes = {
10362 * Default sort that does nothing
10363 * @param {Mixed} s The value being converted
10364 * @return {Mixed} The comparison value
10366 none : function(s){
10371 * The regular expression used to strip tags
10375 stripTagsRE : /<\/?[^>]+>/gi,
10378 * Strips all HTML tags to sort on text only
10379 * @param {Mixed} s The value being converted
10380 * @return {String} The comparison value
10382 asText : function(s){
10383 return String(s).replace(this.stripTagsRE, "");
10387 * Strips all HTML tags to sort on text only - Case insensitive
10388 * @param {Mixed} s The value being converted
10389 * @return {String} The comparison value
10391 asUCText : function(s){
10392 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10396 * Case insensitive string
10397 * @param {Mixed} s The value being converted
10398 * @return {String} The comparison value
10400 asUCString : function(s) {
10401 return String(s).toUpperCase();
10406 * @param {Mixed} s The value being converted
10407 * @return {Number} The comparison value
10409 asDate : function(s) {
10413 if(s instanceof Date){
10414 return s.getTime();
10416 return Date.parse(String(s));
10421 * @param {Mixed} s The value being converted
10422 * @return {Float} The comparison value
10424 asFloat : function(s) {
10425 var val = parseFloat(String(s).replace(/,/g, ""));
10434 * @param {Mixed} s The value being converted
10435 * @return {Number} The comparison value
10437 asInt : function(s) {
10438 var val = parseInt(String(s).replace(/,/g, ""));
10446 * Ext JS Library 1.1.1
10447 * Copyright(c) 2006-2007, Ext JS, LLC.
10449 * Originally Released Under LGPL - original licence link has changed is not relivant.
10452 * <script type="text/javascript">
10456 * @class Roo.data.Record
10457 * Instances of this class encapsulate both record <em>definition</em> information, and record
10458 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10459 * to access Records cached in an {@link Roo.data.Store} object.<br>
10461 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10462 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10465 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10467 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10468 * {@link #create}. The parameters are the same.
10469 * @param {Array} data An associative Array of data values keyed by the field name.
10470 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10471 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10472 * not specified an integer id is generated.
10474 Roo.data.Record = function(data, id){
10475 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10480 * Generate a constructor for a specific record layout.
10481 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10482 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10483 * Each field definition object may contain the following properties: <ul>
10484 * <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,
10485 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10486 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10487 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10488 * is being used, then this is a string containing the javascript expression to reference the data relative to
10489 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10490 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10491 * this may be omitted.</p></li>
10492 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10493 * <ul><li>auto (Default, implies no conversion)</li>
10498 * <li>date</li></ul></p></li>
10499 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10500 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10501 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10502 * by the Reader into an object that will be stored in the Record. It is passed the
10503 * following parameters:<ul>
10504 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10506 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10508 * <br>usage:<br><pre><code>
10509 var TopicRecord = Roo.data.Record.create(
10510 {name: 'title', mapping: 'topic_title'},
10511 {name: 'author', mapping: 'username'},
10512 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10513 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10514 {name: 'lastPoster', mapping: 'user2'},
10515 {name: 'excerpt', mapping: 'post_text'}
10518 var myNewRecord = new TopicRecord({
10519 title: 'Do my job please',
10522 lastPost: new Date(),
10523 lastPoster: 'Animal',
10524 excerpt: 'No way dude!'
10526 myStore.add(myNewRecord);
10531 Roo.data.Record.create = function(o){
10532 var f = function(){
10533 f.superclass.constructor.apply(this, arguments);
10535 Roo.extend(f, Roo.data.Record);
10536 var p = f.prototype;
10537 p.fields = new Roo.util.MixedCollection(false, function(field){
10540 for(var i = 0, len = o.length; i < len; i++){
10541 p.fields.add(new Roo.data.Field(o[i]));
10543 f.getField = function(name){
10544 return p.fields.get(name);
10549 Roo.data.Record.AUTO_ID = 1000;
10550 Roo.data.Record.EDIT = 'edit';
10551 Roo.data.Record.REJECT = 'reject';
10552 Roo.data.Record.COMMIT = 'commit';
10554 Roo.data.Record.prototype = {
10556 * Readonly flag - true if this record has been modified.
10565 join : function(store){
10566 this.store = store;
10570 * Set the named field to the specified value.
10571 * @param {String} name The name of the field to set.
10572 * @param {Object} value The value to set the field to.
10574 set : function(name, value){
10575 if(this.data[name] == value){
10579 if(!this.modified){
10580 this.modified = {};
10582 if(typeof this.modified[name] == 'undefined'){
10583 this.modified[name] = this.data[name];
10585 this.data[name] = value;
10586 if(!this.editing && this.store){
10587 this.store.afterEdit(this);
10592 * Get the value of the named field.
10593 * @param {String} name The name of the field to get the value of.
10594 * @return {Object} The value of the field.
10596 get : function(name){
10597 return this.data[name];
10601 beginEdit : function(){
10602 this.editing = true;
10603 this.modified = {};
10607 cancelEdit : function(){
10608 this.editing = false;
10609 delete this.modified;
10613 endEdit : function(){
10614 this.editing = false;
10615 if(this.dirty && this.store){
10616 this.store.afterEdit(this);
10621 * Usually called by the {@link Roo.data.Store} which owns the Record.
10622 * Rejects all changes made to the Record since either creation, or the last commit operation.
10623 * Modified fields are reverted to their original values.
10625 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10626 * of reject operations.
10628 reject : function(){
10629 var m = this.modified;
10631 if(typeof m[n] != "function"){
10632 this.data[n] = m[n];
10635 this.dirty = false;
10636 delete this.modified;
10637 this.editing = false;
10639 this.store.afterReject(this);
10644 * Usually called by the {@link Roo.data.Store} which owns the Record.
10645 * Commits all changes made to the Record since either creation, or the last commit operation.
10647 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10648 * of commit operations.
10650 commit : function(){
10651 this.dirty = false;
10652 delete this.modified;
10653 this.editing = false;
10655 this.store.afterCommit(this);
10660 hasError : function(){
10661 return this.error != null;
10665 clearError : function(){
10670 * Creates a copy of this record.
10671 * @param {String} id (optional) A new record id if you don't want to use this record's id
10674 copy : function(newId) {
10675 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10679 * Ext JS Library 1.1.1
10680 * Copyright(c) 2006-2007, Ext JS, LLC.
10682 * Originally Released Under LGPL - original licence link has changed is not relivant.
10685 * <script type="text/javascript">
10691 * @class Roo.data.Store
10692 * @extends Roo.util.Observable
10693 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10694 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10696 * 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
10697 * has no knowledge of the format of the data returned by the Proxy.<br>
10699 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10700 * instances from the data object. These records are cached and made available through accessor functions.
10702 * Creates a new Store.
10703 * @param {Object} config A config object containing the objects needed for the Store to access data,
10704 * and read the data into Records.
10706 Roo.data.Store = function(config){
10707 this.data = new Roo.util.MixedCollection(false);
10708 this.data.getKey = function(o){
10711 this.baseParams = {};
10713 this.paramNames = {
10718 "multisort" : "_multisort"
10721 if(config && config.data){
10722 this.inlineData = config.data;
10723 delete config.data;
10726 Roo.apply(this, config);
10728 if(this.reader){ // reader passed
10729 this.reader = Roo.factory(this.reader, Roo.data);
10730 this.reader.xmodule = this.xmodule || false;
10731 if(!this.recordType){
10732 this.recordType = this.reader.recordType;
10734 if(this.reader.onMetaChange){
10735 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10739 if(this.recordType){
10740 this.fields = this.recordType.prototype.fields;
10742 this.modified = [];
10746 * @event datachanged
10747 * Fires when the data cache has changed, and a widget which is using this Store
10748 * as a Record cache should refresh its view.
10749 * @param {Store} this
10751 datachanged : true,
10753 * @event metachange
10754 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10755 * @param {Store} this
10756 * @param {Object} meta The JSON metadata
10761 * Fires when Records have been added to the Store
10762 * @param {Store} this
10763 * @param {Roo.data.Record[]} records The array of Records added
10764 * @param {Number} index The index at which the record(s) were added
10769 * Fires when a Record has been removed from the Store
10770 * @param {Store} this
10771 * @param {Roo.data.Record} record The Record that was removed
10772 * @param {Number} index The index at which the record was removed
10777 * Fires when a Record has been updated
10778 * @param {Store} this
10779 * @param {Roo.data.Record} record The Record that was updated
10780 * @param {String} operation The update operation being performed. Value may be one of:
10782 Roo.data.Record.EDIT
10783 Roo.data.Record.REJECT
10784 Roo.data.Record.COMMIT
10790 * Fires when the data cache has been cleared.
10791 * @param {Store} this
10795 * @event beforeload
10796 * Fires before a request is made for a new data object. If the beforeload handler returns false
10797 * the load action will be canceled.
10798 * @param {Store} this
10799 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10803 * @event beforeloadadd
10804 * Fires after a new set of Records has been loaded.
10805 * @param {Store} this
10806 * @param {Roo.data.Record[]} records The Records that were loaded
10807 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10809 beforeloadadd : true,
10812 * Fires after a new set of Records has been loaded, before they are added to the store.
10813 * @param {Store} this
10814 * @param {Roo.data.Record[]} records The Records that were loaded
10815 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10816 * @params {Object} return from reader
10820 * @event loadexception
10821 * Fires if an exception occurs in the Proxy during loading.
10822 * Called with the signature of the Proxy's "loadexception" event.
10823 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10826 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10827 * @param {Object} load options
10828 * @param {Object} jsonData from your request (normally this contains the Exception)
10830 loadexception : true
10834 this.proxy = Roo.factory(this.proxy, Roo.data);
10835 this.proxy.xmodule = this.xmodule || false;
10836 this.relayEvents(this.proxy, ["loadexception"]);
10838 this.sortToggle = {};
10839 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10841 Roo.data.Store.superclass.constructor.call(this);
10843 if(this.inlineData){
10844 this.loadData(this.inlineData);
10845 delete this.inlineData;
10849 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10851 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10852 * without a remote query - used by combo/forms at present.
10856 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10859 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10862 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10863 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10866 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10867 * on any HTTP request
10870 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10873 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10877 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10878 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10880 remoteSort : false,
10883 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10884 * loaded or when a record is removed. (defaults to false).
10886 pruneModifiedRecords : false,
10889 lastOptions : null,
10892 * Add Records to the Store and fires the add event.
10893 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10895 add : function(records){
10896 records = [].concat(records);
10897 for(var i = 0, len = records.length; i < len; i++){
10898 records[i].join(this);
10900 var index = this.data.length;
10901 this.data.addAll(records);
10902 this.fireEvent("add", this, records, index);
10906 * Remove a Record from the Store and fires the remove event.
10907 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10909 remove : function(record){
10910 var index = this.data.indexOf(record);
10911 this.data.removeAt(index);
10912 if(this.pruneModifiedRecords){
10913 this.modified.remove(record);
10915 this.fireEvent("remove", this, record, index);
10919 * Remove all Records from the Store and fires the clear event.
10921 removeAll : function(){
10923 if(this.pruneModifiedRecords){
10924 this.modified = [];
10926 this.fireEvent("clear", this);
10930 * Inserts Records to the Store at the given index and fires the add event.
10931 * @param {Number} index The start index at which to insert the passed Records.
10932 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10934 insert : function(index, records){
10935 records = [].concat(records);
10936 for(var i = 0, len = records.length; i < len; i++){
10937 this.data.insert(index, records[i]);
10938 records[i].join(this);
10940 this.fireEvent("add", this, records, index);
10944 * Get the index within the cache of the passed Record.
10945 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10946 * @return {Number} The index of the passed Record. Returns -1 if not found.
10948 indexOf : function(record){
10949 return this.data.indexOf(record);
10953 * Get the index within the cache of the Record with the passed id.
10954 * @param {String} id The id of the Record to find.
10955 * @return {Number} The index of the Record. Returns -1 if not found.
10957 indexOfId : function(id){
10958 return this.data.indexOfKey(id);
10962 * Get the Record with the specified id.
10963 * @param {String} id The id of the Record to find.
10964 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10966 getById : function(id){
10967 return this.data.key(id);
10971 * Get the Record at the specified index.
10972 * @param {Number} index The index of the Record to find.
10973 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10975 getAt : function(index){
10976 return this.data.itemAt(index);
10980 * Returns a range of Records between specified indices.
10981 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10982 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10983 * @return {Roo.data.Record[]} An array of Records
10985 getRange : function(start, end){
10986 return this.data.getRange(start, end);
10990 storeOptions : function(o){
10991 o = Roo.apply({}, o);
10994 this.lastOptions = o;
10998 * Loads the Record cache from the configured Proxy using the configured Reader.
11000 * If using remote paging, then the first load call must specify the <em>start</em>
11001 * and <em>limit</em> properties in the options.params property to establish the initial
11002 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11004 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11005 * and this call will return before the new data has been loaded. Perform any post-processing
11006 * in a callback function, or in a "load" event handler.</strong>
11008 * @param {Object} options An object containing properties which control loading options:<ul>
11009 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11010 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11011 * passed the following arguments:<ul>
11012 * <li>r : Roo.data.Record[]</li>
11013 * <li>options: Options object from the load call</li>
11014 * <li>success: Boolean success indicator</li></ul></li>
11015 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11016 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11019 load : function(options){
11020 options = options || {};
11021 if(this.fireEvent("beforeload", this, options) !== false){
11022 this.storeOptions(options);
11023 var p = Roo.apply(options.params || {}, this.baseParams);
11024 // if meta was not loaded from remote source.. try requesting it.
11025 if (!this.reader.metaFromRemote) {
11026 p._requestMeta = 1;
11028 if(this.sortInfo && this.remoteSort){
11029 var pn = this.paramNames;
11030 p[pn["sort"]] = this.sortInfo.field;
11031 p[pn["dir"]] = this.sortInfo.direction;
11033 if (this.multiSort) {
11034 var pn = this.paramNames;
11035 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11038 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11043 * Reloads the Record cache from the configured Proxy using the configured Reader and
11044 * the options from the last load operation performed.
11045 * @param {Object} options (optional) An object containing properties which may override the options
11046 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11047 * the most recently used options are reused).
11049 reload : function(options){
11050 this.load(Roo.applyIf(options||{}, this.lastOptions));
11054 // Called as a callback by the Reader during a load operation.
11055 loadRecords : function(o, options, success){
11056 if(!o || success === false){
11057 if(success !== false){
11058 this.fireEvent("load", this, [], options, o);
11060 if(options.callback){
11061 options.callback.call(options.scope || this, [], options, false);
11065 // if data returned failure - throw an exception.
11066 if (o.success === false) {
11067 // show a message if no listener is registered.
11068 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11069 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11071 // loadmask wil be hooked into this..
11072 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11075 var r = o.records, t = o.totalRecords || r.length;
11077 this.fireEvent("beforeloadadd", this, r, options, o);
11079 if(!options || options.add !== true){
11080 if(this.pruneModifiedRecords){
11081 this.modified = [];
11083 for(var i = 0, len = r.length; i < len; i++){
11087 this.data = this.snapshot;
11088 delete this.snapshot;
11091 this.data.addAll(r);
11092 this.totalLength = t;
11094 this.fireEvent("datachanged", this);
11096 this.totalLength = Math.max(t, this.data.length+r.length);
11100 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11102 var e = new Roo.data.Record({});
11104 e.set(this.parent.displayField, this.parent.emptyTitle);
11105 e.set(this.parent.valueField, '');
11110 this.fireEvent("load", this, r, options, o);
11111 if(options.callback){
11112 options.callback.call(options.scope || this, r, options, true);
11118 * Loads data from a passed data block. A Reader which understands the format of the data
11119 * must have been configured in the constructor.
11120 * @param {Object} data The data block from which to read the Records. The format of the data expected
11121 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11122 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11124 loadData : function(o, append){
11125 var r = this.reader.readRecords(o);
11126 this.loadRecords(r, {add: append}, true);
11130 * Gets the number of cached records.
11132 * <em>If using paging, this may not be the total size of the dataset. If the data object
11133 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11134 * the data set size</em>
11136 getCount : function(){
11137 return this.data.length || 0;
11141 * Gets the total number of records in the dataset as returned by the server.
11143 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11144 * the dataset size</em>
11146 getTotalCount : function(){
11147 return this.totalLength || 0;
11151 * Returns the sort state of the Store as an object with two properties:
11153 field {String} The name of the field by which the Records are sorted
11154 direction {String} The sort order, "ASC" or "DESC"
11157 getSortState : function(){
11158 return this.sortInfo;
11162 applySort : function(){
11163 if(this.sortInfo && !this.remoteSort){
11164 var s = this.sortInfo, f = s.field;
11165 var st = this.fields.get(f).sortType;
11166 var fn = function(r1, r2){
11167 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11168 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11170 this.data.sort(s.direction, fn);
11171 if(this.snapshot && this.snapshot != this.data){
11172 this.snapshot.sort(s.direction, fn);
11178 * Sets the default sort column and order to be used by the next load operation.
11179 * @param {String} fieldName The name of the field to sort by.
11180 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11182 setDefaultSort : function(field, dir){
11183 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11187 * Sort the Records.
11188 * If remote sorting is used, the sort is performed on the server, and the cache is
11189 * reloaded. If local sorting is used, the cache is sorted internally.
11190 * @param {String} fieldName The name of the field to sort by.
11191 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11193 sort : function(fieldName, dir){
11194 var f = this.fields.get(fieldName);
11196 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11198 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11199 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11204 this.sortToggle[f.name] = dir;
11205 this.sortInfo = {field: f.name, direction: dir};
11206 if(!this.remoteSort){
11208 this.fireEvent("datachanged", this);
11210 this.load(this.lastOptions);
11215 * Calls the specified function for each of the Records in the cache.
11216 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11217 * Returning <em>false</em> aborts and exits the iteration.
11218 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11220 each : function(fn, scope){
11221 this.data.each(fn, scope);
11225 * Gets all records modified since the last commit. Modified records are persisted across load operations
11226 * (e.g., during paging).
11227 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11229 getModifiedRecords : function(){
11230 return this.modified;
11234 createFilterFn : function(property, value, anyMatch){
11235 if(!value.exec){ // not a regex
11236 value = String(value);
11237 if(value.length == 0){
11240 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11242 return function(r){
11243 return value.test(r.data[property]);
11248 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11249 * @param {String} property A field on your records
11250 * @param {Number} start The record index to start at (defaults to 0)
11251 * @param {Number} end The last record index to include (defaults to length - 1)
11252 * @return {Number} The sum
11254 sum : function(property, start, end){
11255 var rs = this.data.items, v = 0;
11256 start = start || 0;
11257 end = (end || end === 0) ? end : rs.length-1;
11259 for(var i = start; i <= end; i++){
11260 v += (rs[i].data[property] || 0);
11266 * Filter the records by a specified property.
11267 * @param {String} field A field on your records
11268 * @param {String/RegExp} value Either a string that the field
11269 * should start with or a RegExp to test against the field
11270 * @param {Boolean} anyMatch True to match any part not just the beginning
11272 filter : function(property, value, anyMatch){
11273 var fn = this.createFilterFn(property, value, anyMatch);
11274 return fn ? this.filterBy(fn) : this.clearFilter();
11278 * Filter by a function. The specified function will be called with each
11279 * record in this data source. If the function returns true the record is included,
11280 * otherwise it is filtered.
11281 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11282 * @param {Object} scope (optional) The scope of the function (defaults to this)
11284 filterBy : function(fn, scope){
11285 this.snapshot = this.snapshot || this.data;
11286 this.data = this.queryBy(fn, scope||this);
11287 this.fireEvent("datachanged", this);
11291 * Query the records by a specified property.
11292 * @param {String} field A field on your records
11293 * @param {String/RegExp} value Either a string that the field
11294 * should start with or a RegExp to test against the field
11295 * @param {Boolean} anyMatch True to match any part not just the beginning
11296 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11298 query : function(property, value, anyMatch){
11299 var fn = this.createFilterFn(property, value, anyMatch);
11300 return fn ? this.queryBy(fn) : this.data.clone();
11304 * Query by a function. The specified function will be called with each
11305 * record in this data source. If the function returns true the record is included
11307 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11308 * @param {Object} scope (optional) The scope of the function (defaults to this)
11309 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11311 queryBy : function(fn, scope){
11312 var data = this.snapshot || this.data;
11313 return data.filterBy(fn, scope||this);
11317 * Collects unique values for a particular dataIndex from this store.
11318 * @param {String} dataIndex The property to collect
11319 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11320 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11321 * @return {Array} An array of the unique values
11323 collect : function(dataIndex, allowNull, bypassFilter){
11324 var d = (bypassFilter === true && this.snapshot) ?
11325 this.snapshot.items : this.data.items;
11326 var v, sv, r = [], l = {};
11327 for(var i = 0, len = d.length; i < len; i++){
11328 v = d[i].data[dataIndex];
11330 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11339 * Revert to a view of the Record cache with no filtering applied.
11340 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11342 clearFilter : function(suppressEvent){
11343 if(this.snapshot && this.snapshot != this.data){
11344 this.data = this.snapshot;
11345 delete this.snapshot;
11346 if(suppressEvent !== true){
11347 this.fireEvent("datachanged", this);
11353 afterEdit : function(record){
11354 if(this.modified.indexOf(record) == -1){
11355 this.modified.push(record);
11357 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11361 afterReject : function(record){
11362 this.modified.remove(record);
11363 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11367 afterCommit : function(record){
11368 this.modified.remove(record);
11369 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11373 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11374 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11376 commitChanges : function(){
11377 var m = this.modified.slice(0);
11378 this.modified = [];
11379 for(var i = 0, len = m.length; i < len; i++){
11385 * Cancel outstanding changes on all changed records.
11387 rejectChanges : function(){
11388 var m = this.modified.slice(0);
11389 this.modified = [];
11390 for(var i = 0, len = m.length; i < len; i++){
11395 onMetaChange : function(meta, rtype, o){
11396 this.recordType = rtype;
11397 this.fields = rtype.prototype.fields;
11398 delete this.snapshot;
11399 this.sortInfo = meta.sortInfo || this.sortInfo;
11400 this.modified = [];
11401 this.fireEvent('metachange', this, this.reader.meta);
11404 moveIndex : function(data, type)
11406 var index = this.indexOf(data);
11408 var newIndex = index + type;
11412 this.insert(newIndex, data);
11417 * Ext JS Library 1.1.1
11418 * Copyright(c) 2006-2007, Ext JS, LLC.
11420 * Originally Released Under LGPL - original licence link has changed is not relivant.
11423 * <script type="text/javascript">
11427 * @class Roo.data.SimpleStore
11428 * @extends Roo.data.Store
11429 * Small helper class to make creating Stores from Array data easier.
11430 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11431 * @cfg {Array} fields An array of field definition objects, or field name strings.
11432 * @cfg {Array} data The multi-dimensional array of data
11434 * @param {Object} config
11436 Roo.data.SimpleStore = function(config){
11437 Roo.data.SimpleStore.superclass.constructor.call(this, {
11439 reader: new Roo.data.ArrayReader({
11442 Roo.data.Record.create(config.fields)
11444 proxy : new Roo.data.MemoryProxy(config.data)
11448 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11450 * Ext JS Library 1.1.1
11451 * Copyright(c) 2006-2007, Ext JS, LLC.
11453 * Originally Released Under LGPL - original licence link has changed is not relivant.
11456 * <script type="text/javascript">
11461 * @extends Roo.data.Store
11462 * @class Roo.data.JsonStore
11463 * Small helper class to make creating Stores for JSON data easier. <br/>
11465 var store = new Roo.data.JsonStore({
11466 url: 'get-images.php',
11468 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11471 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11472 * JsonReader and HttpProxy (unless inline data is provided).</b>
11473 * @cfg {Array} fields An array of field definition objects, or field name strings.
11475 * @param {Object} config
11477 Roo.data.JsonStore = function(c){
11478 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11479 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11480 reader: new Roo.data.JsonReader(c, c.fields)
11483 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11485 * Ext JS Library 1.1.1
11486 * Copyright(c) 2006-2007, Ext JS, LLC.
11488 * Originally Released Under LGPL - original licence link has changed is not relivant.
11491 * <script type="text/javascript">
11495 Roo.data.Field = function(config){
11496 if(typeof config == "string"){
11497 config = {name: config};
11499 Roo.apply(this, config);
11502 this.type = "auto";
11505 var st = Roo.data.SortTypes;
11506 // named sortTypes are supported, here we look them up
11507 if(typeof this.sortType == "string"){
11508 this.sortType = st[this.sortType];
11511 // set default sortType for strings and dates
11512 if(!this.sortType){
11515 this.sortType = st.asUCString;
11518 this.sortType = st.asDate;
11521 this.sortType = st.none;
11526 var stripRe = /[\$,%]/g;
11528 // prebuilt conversion function for this field, instead of
11529 // switching every time we're reading a value
11531 var cv, dateFormat = this.dateFormat;
11536 cv = function(v){ return v; };
11539 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11543 return v !== undefined && v !== null && v !== '' ?
11544 parseInt(String(v).replace(stripRe, ""), 10) : '';
11549 return v !== undefined && v !== null && v !== '' ?
11550 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11555 cv = function(v){ return v === true || v === "true" || v == 1; };
11562 if(v instanceof Date){
11566 if(dateFormat == "timestamp"){
11567 return new Date(v*1000);
11569 return Date.parseDate(v, dateFormat);
11571 var parsed = Date.parse(v);
11572 return parsed ? new Date(parsed) : null;
11581 Roo.data.Field.prototype = {
11589 * Ext JS Library 1.1.1
11590 * Copyright(c) 2006-2007, Ext JS, LLC.
11592 * Originally Released Under LGPL - original licence link has changed is not relivant.
11595 * <script type="text/javascript">
11598 // Base class for reading structured data from a data source. This class is intended to be
11599 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11602 * @class Roo.data.DataReader
11603 * Base class for reading structured data from a data source. This class is intended to be
11604 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11607 Roo.data.DataReader = function(meta, recordType){
11611 this.recordType = recordType instanceof Array ?
11612 Roo.data.Record.create(recordType) : recordType;
11615 Roo.data.DataReader.prototype = {
11617 * Create an empty record
11618 * @param {Object} data (optional) - overlay some values
11619 * @return {Roo.data.Record} record created.
11621 newRow : function(d) {
11623 this.recordType.prototype.fields.each(function(c) {
11625 case 'int' : da[c.name] = 0; break;
11626 case 'date' : da[c.name] = new Date(); break;
11627 case 'float' : da[c.name] = 0.0; break;
11628 case 'boolean' : da[c.name] = false; break;
11629 default : da[c.name] = ""; break;
11633 return new this.recordType(Roo.apply(da, d));
11638 * Ext JS Library 1.1.1
11639 * Copyright(c) 2006-2007, Ext JS, LLC.
11641 * Originally Released Under LGPL - original licence link has changed is not relivant.
11644 * <script type="text/javascript">
11648 * @class Roo.data.DataProxy
11649 * @extends Roo.data.Observable
11650 * This class is an abstract base class for implementations which provide retrieval of
11651 * unformatted data objects.<br>
11653 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11654 * (of the appropriate type which knows how to parse the data object) to provide a block of
11655 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11657 * Custom implementations must implement the load method as described in
11658 * {@link Roo.data.HttpProxy#load}.
11660 Roo.data.DataProxy = function(){
11663 * @event beforeload
11664 * Fires before a network request is made to retrieve a data object.
11665 * @param {Object} This DataProxy object.
11666 * @param {Object} params The params parameter to the load function.
11671 * Fires before the load method's callback is called.
11672 * @param {Object} This DataProxy object.
11673 * @param {Object} o The data object.
11674 * @param {Object} arg The callback argument object passed to the load function.
11678 * @event loadexception
11679 * Fires if an Exception occurs during data retrieval.
11680 * @param {Object} This DataProxy object.
11681 * @param {Object} o The data object.
11682 * @param {Object} arg The callback argument object passed to the load function.
11683 * @param {Object} e The Exception.
11685 loadexception : true
11687 Roo.data.DataProxy.superclass.constructor.call(this);
11690 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11693 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11697 * Ext JS Library 1.1.1
11698 * Copyright(c) 2006-2007, Ext JS, LLC.
11700 * Originally Released Under LGPL - original licence link has changed is not relivant.
11703 * <script type="text/javascript">
11706 * @class Roo.data.MemoryProxy
11707 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11708 * to the Reader when its load method is called.
11710 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11712 Roo.data.MemoryProxy = function(data){
11716 Roo.data.MemoryProxy.superclass.constructor.call(this);
11720 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11723 * Load data from the requested source (in this case an in-memory
11724 * data object passed to the constructor), read the data object into
11725 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11726 * process that block using the passed callback.
11727 * @param {Object} params This parameter is not used by the MemoryProxy class.
11728 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11729 * object into a block of Roo.data.Records.
11730 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11731 * The function must be passed <ul>
11732 * <li>The Record block object</li>
11733 * <li>The "arg" argument from the load function</li>
11734 * <li>A boolean success indicator</li>
11736 * @param {Object} scope The scope in which to call the callback
11737 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11739 load : function(params, reader, callback, scope, arg){
11740 params = params || {};
11743 result = reader.readRecords(this.data);
11745 this.fireEvent("loadexception", this, arg, null, e);
11746 callback.call(scope, null, arg, false);
11749 callback.call(scope, result, arg, true);
11753 update : function(params, records){
11758 * Ext JS Library 1.1.1
11759 * Copyright(c) 2006-2007, Ext JS, LLC.
11761 * Originally Released Under LGPL - original licence link has changed is not relivant.
11764 * <script type="text/javascript">
11767 * @class Roo.data.HttpProxy
11768 * @extends Roo.data.DataProxy
11769 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11770 * configured to reference a certain URL.<br><br>
11772 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11773 * from which the running page was served.<br><br>
11775 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11777 * Be aware that to enable the browser to parse an XML document, the server must set
11778 * the Content-Type header in the HTTP response to "text/xml".
11780 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11781 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11782 * will be used to make the request.
11784 Roo.data.HttpProxy = function(conn){
11785 Roo.data.HttpProxy.superclass.constructor.call(this);
11786 // is conn a conn config or a real conn?
11788 this.useAjax = !conn || !conn.events;
11792 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11793 // thse are take from connection...
11796 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11799 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11800 * extra parameters to each request made by this object. (defaults to undefined)
11803 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11804 * to each request made by this object. (defaults to undefined)
11807 * @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)
11810 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11813 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11819 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11823 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11824 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11825 * a finer-grained basis than the DataProxy events.
11827 getConnection : function(){
11828 return this.useAjax ? Roo.Ajax : this.conn;
11832 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11833 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11834 * process that block using the passed callback.
11835 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11836 * for the request to the remote server.
11837 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11838 * object into a block of Roo.data.Records.
11839 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11840 * The function must be passed <ul>
11841 * <li>The Record block object</li>
11842 * <li>The "arg" argument from the load function</li>
11843 * <li>A boolean success indicator</li>
11845 * @param {Object} scope The scope in which to call the callback
11846 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11848 load : function(params, reader, callback, scope, arg){
11849 if(this.fireEvent("beforeload", this, params) !== false){
11851 params : params || {},
11853 callback : callback,
11858 callback : this.loadResponse,
11862 Roo.applyIf(o, this.conn);
11863 if(this.activeRequest){
11864 Roo.Ajax.abort(this.activeRequest);
11866 this.activeRequest = Roo.Ajax.request(o);
11868 this.conn.request(o);
11871 callback.call(scope||this, null, arg, false);
11876 loadResponse : function(o, success, response){
11877 delete this.activeRequest;
11879 this.fireEvent("loadexception", this, o, response);
11880 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11885 result = o.reader.read(response);
11887 this.fireEvent("loadexception", this, o, response, e);
11888 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11892 this.fireEvent("load", this, o, o.request.arg);
11893 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11897 update : function(dataSet){
11902 updateResponse : function(dataSet){
11907 * Ext JS Library 1.1.1
11908 * Copyright(c) 2006-2007, Ext JS, LLC.
11910 * Originally Released Under LGPL - original licence link has changed is not relivant.
11913 * <script type="text/javascript">
11917 * @class Roo.data.ScriptTagProxy
11918 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11919 * other than the originating domain of the running page.<br><br>
11921 * <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
11922 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11924 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11925 * source code that is used as the source inside a <script> tag.<br><br>
11927 * In order for the browser to process the returned data, the server must wrap the data object
11928 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11929 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11930 * depending on whether the callback name was passed:
11933 boolean scriptTag = false;
11934 String cb = request.getParameter("callback");
11937 response.setContentType("text/javascript");
11939 response.setContentType("application/x-json");
11941 Writer out = response.getWriter();
11943 out.write(cb + "(");
11945 out.print(dataBlock.toJsonString());
11952 * @param {Object} config A configuration object.
11954 Roo.data.ScriptTagProxy = function(config){
11955 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11956 Roo.apply(this, config);
11957 this.head = document.getElementsByTagName("head")[0];
11960 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11962 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11964 * @cfg {String} url The URL from which to request the data object.
11967 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11971 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11972 * the server the name of the callback function set up by the load call to process the returned data object.
11973 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11974 * javascript output which calls this named function passing the data object as its only parameter.
11976 callbackParam : "callback",
11978 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11979 * name to the request.
11984 * Load data from the configured URL, read the data object into
11985 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11986 * process that block using the passed callback.
11987 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11988 * for the request to the remote server.
11989 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11990 * object into a block of Roo.data.Records.
11991 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11992 * The function must be passed <ul>
11993 * <li>The Record block object</li>
11994 * <li>The "arg" argument from the load function</li>
11995 * <li>A boolean success indicator</li>
11997 * @param {Object} scope The scope in which to call the callback
11998 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12000 load : function(params, reader, callback, scope, arg){
12001 if(this.fireEvent("beforeload", this, params) !== false){
12003 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12005 var url = this.url;
12006 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12008 url += "&_dc=" + (new Date().getTime());
12010 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12013 cb : "stcCallback"+transId,
12014 scriptId : "stcScript"+transId,
12018 callback : callback,
12024 window[trans.cb] = function(o){
12025 conn.handleResponse(o, trans);
12028 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12030 if(this.autoAbort !== false){
12034 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12036 var script = document.createElement("script");
12037 script.setAttribute("src", url);
12038 script.setAttribute("type", "text/javascript");
12039 script.setAttribute("id", trans.scriptId);
12040 this.head.appendChild(script);
12042 this.trans = trans;
12044 callback.call(scope||this, null, arg, false);
12049 isLoading : function(){
12050 return this.trans ? true : false;
12054 * Abort the current server request.
12056 abort : function(){
12057 if(this.isLoading()){
12058 this.destroyTrans(this.trans);
12063 destroyTrans : function(trans, isLoaded){
12064 this.head.removeChild(document.getElementById(trans.scriptId));
12065 clearTimeout(trans.timeoutId);
12067 window[trans.cb] = undefined;
12069 delete window[trans.cb];
12072 // if hasn't been loaded, wait for load to remove it to prevent script error
12073 window[trans.cb] = function(){
12074 window[trans.cb] = undefined;
12076 delete window[trans.cb];
12083 handleResponse : function(o, trans){
12084 this.trans = false;
12085 this.destroyTrans(trans, true);
12088 result = trans.reader.readRecords(o);
12090 this.fireEvent("loadexception", this, o, trans.arg, e);
12091 trans.callback.call(trans.scope||window, null, trans.arg, false);
12094 this.fireEvent("load", this, o, trans.arg);
12095 trans.callback.call(trans.scope||window, result, trans.arg, true);
12099 handleFailure : function(trans){
12100 this.trans = false;
12101 this.destroyTrans(trans, false);
12102 this.fireEvent("loadexception", this, null, trans.arg);
12103 trans.callback.call(trans.scope||window, null, trans.arg, false);
12107 * Ext JS Library 1.1.1
12108 * Copyright(c) 2006-2007, Ext JS, LLC.
12110 * Originally Released Under LGPL - original licence link has changed is not relivant.
12113 * <script type="text/javascript">
12117 * @class Roo.data.JsonReader
12118 * @extends Roo.data.DataReader
12119 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12120 * based on mappings in a provided Roo.data.Record constructor.
12122 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12123 * in the reply previously.
12128 var RecordDef = Roo.data.Record.create([
12129 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12130 {name: 'occupation'} // This field will use "occupation" as the mapping.
12132 var myReader = new Roo.data.JsonReader({
12133 totalProperty: "results", // The property which contains the total dataset size (optional)
12134 root: "rows", // The property which contains an Array of row objects
12135 id: "id" // The property within each row object that provides an ID for the record (optional)
12139 * This would consume a JSON file like this:
12141 { 'results': 2, 'rows': [
12142 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12143 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12146 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12147 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12148 * paged from the remote server.
12149 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12150 * @cfg {String} root name of the property which contains the Array of row objects.
12151 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12152 * @cfg {Array} fields Array of field definition objects
12154 * Create a new JsonReader
12155 * @param {Object} meta Metadata configuration options
12156 * @param {Object} recordType Either an Array of field definition objects,
12157 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12159 Roo.data.JsonReader = function(meta, recordType){
12162 // set some defaults:
12163 Roo.applyIf(meta, {
12164 totalProperty: 'total',
12165 successProperty : 'success',
12170 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12172 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12175 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12176 * Used by Store query builder to append _requestMeta to params.
12179 metaFromRemote : false,
12181 * This method is only used by a DataProxy which has retrieved data from a remote server.
12182 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12183 * @return {Object} data A data block which is used by an Roo.data.Store object as
12184 * a cache of Roo.data.Records.
12186 read : function(response){
12187 var json = response.responseText;
12189 var o = /* eval:var:o */ eval("("+json+")");
12191 throw {message: "JsonReader.read: Json object not found"};
12197 this.metaFromRemote = true;
12198 this.meta = o.metaData;
12199 this.recordType = Roo.data.Record.create(o.metaData.fields);
12200 this.onMetaChange(this.meta, this.recordType, o);
12202 return this.readRecords(o);
12205 // private function a store will implement
12206 onMetaChange : function(meta, recordType, o){
12213 simpleAccess: function(obj, subsc) {
12220 getJsonAccessor: function(){
12222 return function(expr) {
12224 return(re.test(expr))
12225 ? new Function("obj", "return obj." + expr)
12230 return Roo.emptyFn;
12235 * Create a data block containing Roo.data.Records from an XML document.
12236 * @param {Object} o An object which contains an Array of row objects in the property specified
12237 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12238 * which contains the total size of the dataset.
12239 * @return {Object} data A data block which is used by an Roo.data.Store object as
12240 * a cache of Roo.data.Records.
12242 readRecords : function(o){
12244 * After any data loads, the raw JSON data is available for further custom processing.
12248 var s = this.meta, Record = this.recordType,
12249 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12251 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12253 if(s.totalProperty) {
12254 this.getTotal = this.getJsonAccessor(s.totalProperty);
12256 if(s.successProperty) {
12257 this.getSuccess = this.getJsonAccessor(s.successProperty);
12259 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12261 var g = this.getJsonAccessor(s.id);
12262 this.getId = function(rec) {
12264 return (r === undefined || r === "") ? null : r;
12267 this.getId = function(){return null;};
12270 for(var jj = 0; jj < fl; jj++){
12272 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12273 this.ef[jj] = this.getJsonAccessor(map);
12277 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12278 if(s.totalProperty){
12279 var vt = parseInt(this.getTotal(o), 10);
12284 if(s.successProperty){
12285 var vs = this.getSuccess(o);
12286 if(vs === false || vs === 'false'){
12291 for(var i = 0; i < c; i++){
12294 var id = this.getId(n);
12295 for(var j = 0; j < fl; j++){
12297 var v = this.ef[j](n);
12299 Roo.log('missing convert for ' + f.name);
12303 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12305 var record = new Record(values, id);
12307 records[i] = record;
12313 totalRecords : totalRecords
12318 * Ext JS Library 1.1.1
12319 * Copyright(c) 2006-2007, Ext JS, LLC.
12321 * Originally Released Under LGPL - original licence link has changed is not relivant.
12324 * <script type="text/javascript">
12328 * @class Roo.data.ArrayReader
12329 * @extends Roo.data.DataReader
12330 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12331 * Each element of that Array represents a row of data fields. The
12332 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12333 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12337 var RecordDef = Roo.data.Record.create([
12338 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12339 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12341 var myReader = new Roo.data.ArrayReader({
12342 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12346 * This would consume an Array like this:
12348 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12350 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12352 * Create a new JsonReader
12353 * @param {Object} meta Metadata configuration options.
12354 * @param {Object} recordType Either an Array of field definition objects
12355 * as specified to {@link Roo.data.Record#create},
12356 * or an {@link Roo.data.Record} object
12357 * created using {@link Roo.data.Record#create}.
12359 Roo.data.ArrayReader = function(meta, recordType){
12360 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12363 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12365 * Create a data block containing Roo.data.Records from an XML document.
12366 * @param {Object} o An Array of row objects which represents the dataset.
12367 * @return {Object} data A data block which is used by an Roo.data.Store object as
12368 * a cache of Roo.data.Records.
12370 readRecords : function(o){
12371 var sid = this.meta ? this.meta.id : null;
12372 var recordType = this.recordType, fields = recordType.prototype.fields;
12375 for(var i = 0; i < root.length; i++){
12378 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12379 for(var j = 0, jlen = fields.length; j < jlen; j++){
12380 var f = fields.items[j];
12381 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12382 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12384 values[f.name] = v;
12386 var record = new recordType(values, id);
12388 records[records.length] = record;
12392 totalRecords : records.length
12401 * @class Roo.bootstrap.ComboBox
12402 * @extends Roo.bootstrap.TriggerField
12403 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12404 * @cfg {Boolean} append (true|false) default false
12405 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12406 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12407 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12408 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12409 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12410 * @cfg {Boolean} animate default true
12411 * @cfg {Boolean} emptyResultText only for touch device
12412 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12413 * @cfg {String} emptyTitle default ''
12415 * Create a new ComboBox.
12416 * @param {Object} config Configuration options
12418 Roo.bootstrap.ComboBox = function(config){
12419 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12423 * Fires when the dropdown list is expanded
12424 * @param {Roo.bootstrap.ComboBox} combo This combo box
12429 * Fires when the dropdown list is collapsed
12430 * @param {Roo.bootstrap.ComboBox} combo This combo box
12434 * @event beforeselect
12435 * Fires before a list item is selected. Return false to cancel the selection.
12436 * @param {Roo.bootstrap.ComboBox} combo This combo box
12437 * @param {Roo.data.Record} record The data record returned from the underlying store
12438 * @param {Number} index The index of the selected item in the dropdown list
12440 'beforeselect' : true,
12443 * Fires when a list item is selected
12444 * @param {Roo.bootstrap.ComboBox} combo This combo box
12445 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12446 * @param {Number} index The index of the selected item in the dropdown list
12450 * @event beforequery
12451 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12452 * The event object passed has these properties:
12453 * @param {Roo.bootstrap.ComboBox} combo This combo box
12454 * @param {String} query The query
12455 * @param {Boolean} forceAll true to force "all" query
12456 * @param {Boolean} cancel true to cancel the query
12457 * @param {Object} e The query event object
12459 'beforequery': true,
12462 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12463 * @param {Roo.bootstrap.ComboBox} combo This combo box
12468 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12469 * @param {Roo.bootstrap.ComboBox} combo This combo box
12470 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12475 * Fires when the remove value from the combobox array
12476 * @param {Roo.bootstrap.ComboBox} combo This combo box
12480 * @event afterremove
12481 * Fires when the remove value from the combobox array
12482 * @param {Roo.bootstrap.ComboBox} combo This combo box
12484 'afterremove' : true,
12486 * @event specialfilter
12487 * Fires when specialfilter
12488 * @param {Roo.bootstrap.ComboBox} combo This combo box
12490 'specialfilter' : true,
12493 * Fires when tick the element
12494 * @param {Roo.bootstrap.ComboBox} combo This combo box
12498 * @event touchviewdisplay
12499 * Fires when touch view require special display (default is using displayField)
12500 * @param {Roo.bootstrap.ComboBox} combo This combo box
12501 * @param {Object} cfg set html .
12503 'touchviewdisplay' : true
12508 this.tickItems = [];
12510 this.selectedIndex = -1;
12511 if(this.mode == 'local'){
12512 if(config.queryDelay === undefined){
12513 this.queryDelay = 10;
12515 if(config.minChars === undefined){
12521 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12524 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12525 * rendering into an Roo.Editor, defaults to false)
12528 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12529 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12532 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12535 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12536 * the dropdown list (defaults to undefined, with no header element)
12540 * @cfg {String/Roo.Template} tpl The template to use to render the output
12544 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12546 listWidth: undefined,
12548 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12549 * mode = 'remote' or 'text' if mode = 'local')
12551 displayField: undefined,
12554 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12555 * mode = 'remote' or 'value' if mode = 'local').
12556 * Note: use of a valueField requires the user make a selection
12557 * in order for a value to be mapped.
12559 valueField: undefined,
12561 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12566 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12567 * field's data value (defaults to the underlying DOM element's name)
12569 hiddenName: undefined,
12571 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12575 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12577 selectedClass: 'active',
12580 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12584 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12585 * anchor positions (defaults to 'tl-bl')
12587 listAlign: 'tl-bl?',
12589 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12593 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12594 * query specified by the allQuery config option (defaults to 'query')
12596 triggerAction: 'query',
12598 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12599 * (defaults to 4, does not apply if editable = false)
12603 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12604 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12608 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12609 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12613 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12614 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12618 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12619 * when editable = true (defaults to false)
12621 selectOnFocus:false,
12623 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12625 queryParam: 'query',
12627 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12628 * when mode = 'remote' (defaults to 'Loading...')
12630 loadingText: 'Loading...',
12632 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12636 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12640 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12641 * traditional select (defaults to true)
12645 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12649 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12653 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12654 * listWidth has a higher value)
12658 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12659 * allow the user to set arbitrary text into the field (defaults to false)
12661 forceSelection:false,
12663 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12664 * if typeAhead = true (defaults to 250)
12666 typeAheadDelay : 250,
12668 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12669 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12671 valueNotFoundText : undefined,
12673 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12675 blockFocus : false,
12678 * @cfg {Boolean} disableClear Disable showing of clear button.
12680 disableClear : false,
12682 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12684 alwaysQuery : false,
12687 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12692 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12694 invalidClass : "has-warning",
12697 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12699 validClass : "has-success",
12702 * @cfg {Boolean} specialFilter (true|false) special filter default false
12704 specialFilter : false,
12707 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12709 mobileTouchView : true,
12712 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12714 useNativeIOS : false,
12716 ios_options : false,
12728 btnPosition : 'right',
12729 triggerList : true,
12730 showToggleBtn : true,
12732 emptyResultText: 'Empty',
12733 triggerText : 'Select',
12736 // element that contains real text value.. (when hidden is used..)
12738 getAutoCreate : function()
12743 * Render classic select for iso
12746 if(Roo.isIOS && this.useNativeIOS){
12747 cfg = this.getAutoCreateNativeIOS();
12755 if(Roo.isTouch && this.mobileTouchView){
12756 cfg = this.getAutoCreateTouchView();
12763 if(!this.tickable){
12764 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12769 * ComboBox with tickable selections
12772 var align = this.labelAlign || this.parentLabelAlign();
12775 cls : 'form-group roo-combobox-tickable' //input-group
12778 var btn_text_select = '';
12779 var btn_text_done = '';
12780 var btn_text_cancel = '';
12782 if (this.btn_text_show) {
12783 btn_text_select = 'Select';
12784 btn_text_done = 'Done';
12785 btn_text_cancel = 'Cancel';
12790 cls : 'tickable-buttons',
12795 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12796 //html : this.triggerText
12797 html: btn_text_select
12803 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12805 html: btn_text_done
12811 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12813 html: btn_text_cancel
12819 buttons.cn.unshift({
12821 cls: 'roo-select2-search-field-input'
12827 Roo.each(buttons.cn, function(c){
12829 c.cls += ' btn-' + _this.size;
12832 if (_this.disabled) {
12843 cls: 'form-hidden-field'
12847 cls: 'roo-select2-choices',
12851 cls: 'roo-select2-search-field',
12862 cls: 'roo-select2-container input-group roo-select2-container-multi',
12867 // cls: 'typeahead typeahead-long dropdown-menu',
12868 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12873 if(this.hasFeedback && !this.allowBlank){
12877 cls: 'glyphicon form-control-feedback'
12880 combobox.cn.push(feedback);
12884 if (align ==='left' && this.fieldLabel.length) {
12886 cfg.cls += ' roo-form-group-label-left';
12891 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12892 tooltip : 'This field is required'
12897 cls : 'control-label',
12898 html : this.fieldLabel
12910 var labelCfg = cfg.cn[1];
12911 var contentCfg = cfg.cn[2];
12914 if(this.indicatorpos == 'right'){
12920 cls : 'control-label',
12924 html : this.fieldLabel
12928 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12929 tooltip : 'This field is required'
12944 labelCfg = cfg.cn[0];
12945 contentCfg = cfg.cn[1];
12949 if(this.labelWidth > 12){
12950 labelCfg.style = "width: " + this.labelWidth + 'px';
12953 if(this.labelWidth < 13 && this.labelmd == 0){
12954 this.labelmd = this.labelWidth;
12957 if(this.labellg > 0){
12958 labelCfg.cls += ' col-lg-' + this.labellg;
12959 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12962 if(this.labelmd > 0){
12963 labelCfg.cls += ' col-md-' + this.labelmd;
12964 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12967 if(this.labelsm > 0){
12968 labelCfg.cls += ' col-sm-' + this.labelsm;
12969 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12972 if(this.labelxs > 0){
12973 labelCfg.cls += ' col-xs-' + this.labelxs;
12974 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12978 } else if ( this.fieldLabel.length) {
12979 // Roo.log(" label");
12983 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12984 tooltip : 'This field is required'
12988 //cls : 'input-group-addon',
12989 html : this.fieldLabel
12994 if(this.indicatorpos == 'right'){
12998 //cls : 'input-group-addon',
12999 html : this.fieldLabel
13003 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13004 tooltip : 'This field is required'
13013 // Roo.log(" no label && no align");
13020 ['xs','sm','md','lg'].map(function(size){
13021 if (settings[size]) {
13022 cfg.cls += ' col-' + size + '-' + settings[size];
13030 _initEventsCalled : false,
13033 initEvents: function()
13035 if (this._initEventsCalled) { // as we call render... prevent looping...
13038 this._initEventsCalled = true;
13041 throw "can not find store for combo";
13044 this.indicator = this.indicatorEl();
13046 this.store = Roo.factory(this.store, Roo.data);
13047 this.store.parent = this;
13049 // if we are building from html. then this element is so complex, that we can not really
13050 // use the rendered HTML.
13051 // so we have to trash and replace the previous code.
13052 if (Roo.XComponent.build_from_html) {
13053 // remove this element....
13054 var e = this.el.dom, k=0;
13055 while (e ) { e = e.previousSibling; ++k;}
13060 this.rendered = false;
13062 this.render(this.parent().getChildContainer(true), k);
13065 if(Roo.isIOS && this.useNativeIOS){
13066 this.initIOSView();
13074 if(Roo.isTouch && this.mobileTouchView){
13075 this.initTouchView();
13080 this.initTickableEvents();
13084 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13086 if(this.hiddenName){
13088 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13090 this.hiddenField.dom.value =
13091 this.hiddenValue !== undefined ? this.hiddenValue :
13092 this.value !== undefined ? this.value : '';
13094 // prevent input submission
13095 this.el.dom.removeAttribute('name');
13096 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13101 // this.el.dom.setAttribute('autocomplete', 'off');
13104 var cls = 'x-combo-list';
13106 //this.list = new Roo.Layer({
13107 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13113 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13114 _this.list.setWidth(lw);
13117 this.list.on('mouseover', this.onViewOver, this);
13118 this.list.on('mousemove', this.onViewMove, this);
13119 this.list.on('scroll', this.onViewScroll, this);
13122 this.list.swallowEvent('mousewheel');
13123 this.assetHeight = 0;
13126 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13127 this.assetHeight += this.header.getHeight();
13130 this.innerList = this.list.createChild({cls:cls+'-inner'});
13131 this.innerList.on('mouseover', this.onViewOver, this);
13132 this.innerList.on('mousemove', this.onViewMove, this);
13133 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13135 if(this.allowBlank && !this.pageSize && !this.disableClear){
13136 this.footer = this.list.createChild({cls:cls+'-ft'});
13137 this.pageTb = new Roo.Toolbar(this.footer);
13141 this.footer = this.list.createChild({cls:cls+'-ft'});
13142 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13143 {pageSize: this.pageSize});
13147 if (this.pageTb && this.allowBlank && !this.disableClear) {
13149 this.pageTb.add(new Roo.Toolbar.Fill(), {
13150 cls: 'x-btn-icon x-btn-clear',
13152 handler: function()
13155 _this.clearValue();
13156 _this.onSelect(false, -1);
13161 this.assetHeight += this.footer.getHeight();
13166 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13169 this.view = new Roo.View(this.list, this.tpl, {
13170 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13172 //this.view.wrapEl.setDisplayed(false);
13173 this.view.on('click', this.onViewClick, this);
13176 this.store.on('beforeload', this.onBeforeLoad, this);
13177 this.store.on('load', this.onLoad, this);
13178 this.store.on('loadexception', this.onLoadException, this);
13180 if(this.resizable){
13181 this.resizer = new Roo.Resizable(this.list, {
13182 pinned:true, handles:'se'
13184 this.resizer.on('resize', function(r, w, h){
13185 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13186 this.listWidth = w;
13187 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13188 this.restrictHeight();
13190 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13193 if(!this.editable){
13194 this.editable = true;
13195 this.setEditable(false);
13200 if (typeof(this.events.add.listeners) != 'undefined') {
13202 this.addicon = this.wrap.createChild(
13203 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13205 this.addicon.on('click', function(e) {
13206 this.fireEvent('add', this);
13209 if (typeof(this.events.edit.listeners) != 'undefined') {
13211 this.editicon = this.wrap.createChild(
13212 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13213 if (this.addicon) {
13214 this.editicon.setStyle('margin-left', '40px');
13216 this.editicon.on('click', function(e) {
13218 // we fire even if inothing is selected..
13219 this.fireEvent('edit', this, this.lastData );
13225 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13226 "up" : function(e){
13227 this.inKeyMode = true;
13231 "down" : function(e){
13232 if(!this.isExpanded()){
13233 this.onTriggerClick();
13235 this.inKeyMode = true;
13240 "enter" : function(e){
13241 // this.onViewClick();
13245 if(this.fireEvent("specialkey", this, e)){
13246 this.onViewClick(false);
13252 "esc" : function(e){
13256 "tab" : function(e){
13259 if(this.fireEvent("specialkey", this, e)){
13260 this.onViewClick(false);
13268 doRelay : function(foo, bar, hname){
13269 if(hname == 'down' || this.scope.isExpanded()){
13270 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13279 this.queryDelay = Math.max(this.queryDelay || 10,
13280 this.mode == 'local' ? 10 : 250);
13283 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13285 if(this.typeAhead){
13286 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13288 if(this.editable !== false){
13289 this.inputEl().on("keyup", this.onKeyUp, this);
13291 if(this.forceSelection){
13292 this.inputEl().on('blur', this.doForce, this);
13296 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13297 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13301 initTickableEvents: function()
13305 if(this.hiddenName){
13307 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13309 this.hiddenField.dom.value =
13310 this.hiddenValue !== undefined ? this.hiddenValue :
13311 this.value !== undefined ? this.value : '';
13313 // prevent input submission
13314 this.el.dom.removeAttribute('name');
13315 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13320 // this.list = this.el.select('ul.dropdown-menu',true).first();
13322 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13323 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13324 if(this.triggerList){
13325 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13328 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13329 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13331 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13332 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13334 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13335 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13337 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13338 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13339 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13342 this.cancelBtn.hide();
13347 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13348 _this.list.setWidth(lw);
13351 this.list.on('mouseover', this.onViewOver, this);
13352 this.list.on('mousemove', this.onViewMove, this);
13354 this.list.on('scroll', this.onViewScroll, this);
13357 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>';
13360 this.view = new Roo.View(this.list, this.tpl, {
13361 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13364 //this.view.wrapEl.setDisplayed(false);
13365 this.view.on('click', this.onViewClick, this);
13369 this.store.on('beforeload', this.onBeforeLoad, this);
13370 this.store.on('load', this.onLoad, this);
13371 this.store.on('loadexception', this.onLoadException, this);
13374 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13375 "up" : function(e){
13376 this.inKeyMode = true;
13380 "down" : function(e){
13381 this.inKeyMode = true;
13385 "enter" : function(e){
13386 if(this.fireEvent("specialkey", this, e)){
13387 this.onViewClick(false);
13393 "esc" : function(e){
13394 this.onTickableFooterButtonClick(e, false, false);
13397 "tab" : function(e){
13398 this.fireEvent("specialkey", this, e);
13400 this.onTickableFooterButtonClick(e, false, false);
13407 doRelay : function(e, fn, key){
13408 if(this.scope.isExpanded()){
13409 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13418 this.queryDelay = Math.max(this.queryDelay || 10,
13419 this.mode == 'local' ? 10 : 250);
13422 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13424 if(this.typeAhead){
13425 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13428 if(this.editable !== false){
13429 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13432 this.indicator = this.indicatorEl();
13434 if(this.indicator){
13435 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13436 this.indicator.hide();
13441 onDestroy : function(){
13443 this.view.setStore(null);
13444 this.view.el.removeAllListeners();
13445 this.view.el.remove();
13446 this.view.purgeListeners();
13449 this.list.dom.innerHTML = '';
13453 this.store.un('beforeload', this.onBeforeLoad, this);
13454 this.store.un('load', this.onLoad, this);
13455 this.store.un('loadexception', this.onLoadException, this);
13457 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13461 fireKey : function(e){
13462 if(e.isNavKeyPress() && !this.list.isVisible()){
13463 this.fireEvent("specialkey", this, e);
13468 onResize: function(w, h){
13469 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13471 // if(typeof w != 'number'){
13472 // // we do not handle it!?!?
13475 // var tw = this.trigger.getWidth();
13476 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13477 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13479 // this.inputEl().setWidth( this.adjustWidth('input', x));
13481 // //this.trigger.setStyle('left', x+'px');
13483 // if(this.list && this.listWidth === undefined){
13484 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13485 // this.list.setWidth(lw);
13486 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13494 * Allow or prevent the user from directly editing the field text. If false is passed,
13495 * the user will only be able to select from the items defined in the dropdown list. This method
13496 * is the runtime equivalent of setting the 'editable' config option at config time.
13497 * @param {Boolean} value True to allow the user to directly edit the field text
13499 setEditable : function(value){
13500 if(value == this.editable){
13503 this.editable = value;
13505 this.inputEl().dom.setAttribute('readOnly', true);
13506 this.inputEl().on('mousedown', this.onTriggerClick, this);
13507 this.inputEl().addClass('x-combo-noedit');
13509 this.inputEl().dom.setAttribute('readOnly', false);
13510 this.inputEl().un('mousedown', this.onTriggerClick, this);
13511 this.inputEl().removeClass('x-combo-noedit');
13517 onBeforeLoad : function(combo,opts){
13518 if(!this.hasFocus){
13522 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13524 this.restrictHeight();
13525 this.selectedIndex = -1;
13529 onLoad : function(){
13531 this.hasQuery = false;
13533 if(!this.hasFocus){
13537 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13538 this.loading.hide();
13541 if(this.store.getCount() > 0){
13544 this.restrictHeight();
13545 if(this.lastQuery == this.allQuery){
13546 if(this.editable && !this.tickable){
13547 this.inputEl().dom.select();
13551 !this.selectByValue(this.value, true) &&
13554 !this.store.lastOptions ||
13555 typeof(this.store.lastOptions.add) == 'undefined' ||
13556 this.store.lastOptions.add != true
13559 this.select(0, true);
13562 if(this.autoFocus){
13565 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13566 this.taTask.delay(this.typeAheadDelay);
13570 this.onEmptyResults();
13576 onLoadException : function()
13578 this.hasQuery = false;
13580 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13581 this.loading.hide();
13584 if(this.tickable && this.editable){
13589 // only causes errors at present
13590 //Roo.log(this.store.reader.jsonData);
13591 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13593 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13599 onTypeAhead : function(){
13600 if(this.store.getCount() > 0){
13601 var r = this.store.getAt(0);
13602 var newValue = r.data[this.displayField];
13603 var len = newValue.length;
13604 var selStart = this.getRawValue().length;
13606 if(selStart != len){
13607 this.setRawValue(newValue);
13608 this.selectText(selStart, newValue.length);
13614 onSelect : function(record, index){
13616 if(this.fireEvent('beforeselect', this, record, index) !== false){
13618 this.setFromData(index > -1 ? record.data : false);
13621 this.fireEvent('select', this, record, index);
13626 * Returns the currently selected field value or empty string if no value is set.
13627 * @return {String} value The selected value
13629 getValue : function()
13631 if(Roo.isIOS && this.useNativeIOS){
13632 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13636 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13639 if(this.valueField){
13640 return typeof this.value != 'undefined' ? this.value : '';
13642 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13646 getRawValue : function()
13648 if(Roo.isIOS && this.useNativeIOS){
13649 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13652 var v = this.inputEl().getValue();
13658 * Clears any text/value currently set in the field
13660 clearValue : function(){
13662 if(this.hiddenField){
13663 this.hiddenField.dom.value = '';
13666 this.setRawValue('');
13667 this.lastSelectionText = '';
13668 this.lastData = false;
13670 var close = this.closeTriggerEl();
13681 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13682 * will be displayed in the field. If the value does not match the data value of an existing item,
13683 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13684 * Otherwise the field will be blank (although the value will still be set).
13685 * @param {String} value The value to match
13687 setValue : function(v)
13689 if(Roo.isIOS && this.useNativeIOS){
13690 this.setIOSValue(v);
13700 if(this.valueField){
13701 var r = this.findRecord(this.valueField, v);
13703 text = r.data[this.displayField];
13704 }else if(this.valueNotFoundText !== undefined){
13705 text = this.valueNotFoundText;
13708 this.lastSelectionText = text;
13709 if(this.hiddenField){
13710 this.hiddenField.dom.value = v;
13712 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13715 var close = this.closeTriggerEl();
13718 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13724 * @property {Object} the last set data for the element
13729 * Sets the value of the field based on a object which is related to the record format for the store.
13730 * @param {Object} value the value to set as. or false on reset?
13732 setFromData : function(o){
13739 var dv = ''; // display value
13740 var vv = ''; // value value..
13742 if (this.displayField) {
13743 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13745 // this is an error condition!!!
13746 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13749 if(this.valueField){
13750 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13753 var close = this.closeTriggerEl();
13756 if(dv.length || vv * 1 > 0){
13758 this.blockFocus=true;
13764 if(this.hiddenField){
13765 this.hiddenField.dom.value = vv;
13767 this.lastSelectionText = dv;
13768 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13772 // no hidden field.. - we store the value in 'value', but still display
13773 // display field!!!!
13774 this.lastSelectionText = dv;
13775 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13782 reset : function(){
13783 // overridden so that last data is reset..
13790 this.setValue(this.originalValue);
13791 //this.clearInvalid();
13792 this.lastData = false;
13794 this.view.clearSelections();
13800 findRecord : function(prop, value){
13802 if(this.store.getCount() > 0){
13803 this.store.each(function(r){
13804 if(r.data[prop] == value){
13814 getName: function()
13816 // returns hidden if it's set..
13817 if (!this.rendered) {return ''};
13818 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13822 onViewMove : function(e, t){
13823 this.inKeyMode = false;
13827 onViewOver : function(e, t){
13828 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13831 var item = this.view.findItemFromChild(t);
13834 var index = this.view.indexOf(item);
13835 this.select(index, false);
13840 onViewClick : function(view, doFocus, el, e)
13842 var index = this.view.getSelectedIndexes()[0];
13844 var r = this.store.getAt(index);
13848 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13855 Roo.each(this.tickItems, function(v,k){
13857 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13859 _this.tickItems.splice(k, 1);
13861 if(typeof(e) == 'undefined' && view == false){
13862 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13874 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13875 this.tickItems.push(r.data);
13878 if(typeof(e) == 'undefined' && view == false){
13879 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13886 this.onSelect(r, index);
13888 if(doFocus !== false && !this.blockFocus){
13889 this.inputEl().focus();
13894 restrictHeight : function(){
13895 //this.innerList.dom.style.height = '';
13896 //var inner = this.innerList.dom;
13897 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13898 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13899 //this.list.beginUpdate();
13900 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13901 this.list.alignTo(this.inputEl(), this.listAlign);
13902 this.list.alignTo(this.inputEl(), this.listAlign);
13903 //this.list.endUpdate();
13907 onEmptyResults : function(){
13909 if(this.tickable && this.editable){
13910 this.restrictHeight();
13918 * Returns true if the dropdown list is expanded, else false.
13920 isExpanded : function(){
13921 return this.list.isVisible();
13925 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13926 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13927 * @param {String} value The data value of the item to select
13928 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13929 * selected item if it is not currently in view (defaults to true)
13930 * @return {Boolean} True if the value matched an item in the list, else false
13932 selectByValue : function(v, scrollIntoView){
13933 if(v !== undefined && v !== null){
13934 var r = this.findRecord(this.valueField || this.displayField, v);
13936 this.select(this.store.indexOf(r), scrollIntoView);
13944 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13945 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13946 * @param {Number} index The zero-based index of the list item to select
13947 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13948 * selected item if it is not currently in view (defaults to true)
13950 select : function(index, scrollIntoView){
13951 this.selectedIndex = index;
13952 this.view.select(index);
13953 if(scrollIntoView !== false){
13954 var el = this.view.getNode(index);
13956 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13959 this.list.scrollChildIntoView(el, false);
13965 selectNext : function(){
13966 var ct = this.store.getCount();
13968 if(this.selectedIndex == -1){
13970 }else if(this.selectedIndex < ct-1){
13971 this.select(this.selectedIndex+1);
13977 selectPrev : function(){
13978 var ct = this.store.getCount();
13980 if(this.selectedIndex == -1){
13982 }else if(this.selectedIndex != 0){
13983 this.select(this.selectedIndex-1);
13989 onKeyUp : function(e){
13990 if(this.editable !== false && !e.isSpecialKey()){
13991 this.lastKey = e.getKey();
13992 this.dqTask.delay(this.queryDelay);
13997 validateBlur : function(){
13998 return !this.list || !this.list.isVisible();
14002 initQuery : function(){
14004 var v = this.getRawValue();
14006 if(this.tickable && this.editable){
14007 v = this.tickableInputEl().getValue();
14014 doForce : function(){
14015 if(this.inputEl().dom.value.length > 0){
14016 this.inputEl().dom.value =
14017 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14023 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14024 * query allowing the query action to be canceled if needed.
14025 * @param {String} query The SQL query to execute
14026 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14027 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14028 * saved in the current store (defaults to false)
14030 doQuery : function(q, forceAll){
14032 if(q === undefined || q === null){
14037 forceAll: forceAll,
14041 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14046 forceAll = qe.forceAll;
14047 if(forceAll === true || (q.length >= this.minChars)){
14049 this.hasQuery = true;
14051 if(this.lastQuery != q || this.alwaysQuery){
14052 this.lastQuery = q;
14053 if(this.mode == 'local'){
14054 this.selectedIndex = -1;
14056 this.store.clearFilter();
14059 if(this.specialFilter){
14060 this.fireEvent('specialfilter', this);
14065 this.store.filter(this.displayField, q);
14068 this.store.fireEvent("datachanged", this.store);
14075 this.store.baseParams[this.queryParam] = q;
14077 var options = {params : this.getParams(q)};
14080 options.add = true;
14081 options.params.start = this.page * this.pageSize;
14084 this.store.load(options);
14087 * this code will make the page width larger, at the beginning, the list not align correctly,
14088 * we should expand the list on onLoad
14089 * so command out it
14094 this.selectedIndex = -1;
14099 this.loadNext = false;
14103 getParams : function(q){
14105 //p[this.queryParam] = q;
14109 p.limit = this.pageSize;
14115 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14117 collapse : function(){
14118 if(!this.isExpanded()){
14124 this.hasFocus = false;
14128 this.cancelBtn.hide();
14129 this.trigger.show();
14132 this.tickableInputEl().dom.value = '';
14133 this.tickableInputEl().blur();
14138 Roo.get(document).un('mousedown', this.collapseIf, this);
14139 Roo.get(document).un('mousewheel', this.collapseIf, this);
14140 if (!this.editable) {
14141 Roo.get(document).un('keydown', this.listKeyPress, this);
14143 this.fireEvent('collapse', this);
14149 collapseIf : function(e){
14150 var in_combo = e.within(this.el);
14151 var in_list = e.within(this.list);
14152 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14154 if (in_combo || in_list || is_list) {
14155 //e.stopPropagation();
14160 this.onTickableFooterButtonClick(e, false, false);
14168 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14170 expand : function(){
14172 if(this.isExpanded() || !this.hasFocus){
14176 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14177 this.list.setWidth(lw);
14183 this.restrictHeight();
14187 this.tickItems = Roo.apply([], this.item);
14190 this.cancelBtn.show();
14191 this.trigger.hide();
14194 this.tickableInputEl().focus();
14199 Roo.get(document).on('mousedown', this.collapseIf, this);
14200 Roo.get(document).on('mousewheel', this.collapseIf, this);
14201 if (!this.editable) {
14202 Roo.get(document).on('keydown', this.listKeyPress, this);
14205 this.fireEvent('expand', this);
14209 // Implements the default empty TriggerField.onTriggerClick function
14210 onTriggerClick : function(e)
14212 Roo.log('trigger click');
14214 if(this.disabled || !this.triggerList){
14219 this.loadNext = false;
14221 if(this.isExpanded()){
14223 if (!this.blockFocus) {
14224 this.inputEl().focus();
14228 this.hasFocus = true;
14229 if(this.triggerAction == 'all') {
14230 this.doQuery(this.allQuery, true);
14232 this.doQuery(this.getRawValue());
14234 if (!this.blockFocus) {
14235 this.inputEl().focus();
14240 onTickableTriggerClick : function(e)
14247 this.loadNext = false;
14248 this.hasFocus = true;
14250 if(this.triggerAction == 'all') {
14251 this.doQuery(this.allQuery, true);
14253 this.doQuery(this.getRawValue());
14257 onSearchFieldClick : function(e)
14259 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14260 this.onTickableFooterButtonClick(e, false, false);
14264 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14269 this.loadNext = false;
14270 this.hasFocus = true;
14272 if(this.triggerAction == 'all') {
14273 this.doQuery(this.allQuery, true);
14275 this.doQuery(this.getRawValue());
14279 listKeyPress : function(e)
14281 //Roo.log('listkeypress');
14282 // scroll to first matching element based on key pres..
14283 if (e.isSpecialKey()) {
14286 var k = String.fromCharCode(e.getKey()).toUpperCase();
14289 var csel = this.view.getSelectedNodes();
14290 var cselitem = false;
14292 var ix = this.view.indexOf(csel[0]);
14293 cselitem = this.store.getAt(ix);
14294 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14300 this.store.each(function(v) {
14302 // start at existing selection.
14303 if (cselitem.id == v.id) {
14309 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14310 match = this.store.indexOf(v);
14316 if (match === false) {
14317 return true; // no more action?
14320 this.view.select(match);
14321 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14322 sn.scrollIntoView(sn.dom.parentNode, false);
14325 onViewScroll : function(e, t){
14327 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){
14331 this.hasQuery = true;
14333 this.loading = this.list.select('.loading', true).first();
14335 if(this.loading === null){
14336 this.list.createChild({
14338 cls: 'loading roo-select2-more-results roo-select2-active',
14339 html: 'Loading more results...'
14342 this.loading = this.list.select('.loading', true).first();
14344 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14346 this.loading.hide();
14349 this.loading.show();
14354 this.loadNext = true;
14356 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14361 addItem : function(o)
14363 var dv = ''; // display value
14365 if (this.displayField) {
14366 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14368 // this is an error condition!!!
14369 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14376 var choice = this.choices.createChild({
14378 cls: 'roo-select2-search-choice',
14387 cls: 'roo-select2-search-choice-close fa fa-times',
14392 }, this.searchField);
14394 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14396 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14404 this.inputEl().dom.value = '';
14409 onRemoveItem : function(e, _self, o)
14411 e.preventDefault();
14413 this.lastItem = Roo.apply([], this.item);
14415 var index = this.item.indexOf(o.data) * 1;
14418 Roo.log('not this item?!');
14422 this.item.splice(index, 1);
14427 this.fireEvent('remove', this, e);
14433 syncValue : function()
14435 if(!this.item.length){
14442 Roo.each(this.item, function(i){
14443 if(_this.valueField){
14444 value.push(i[_this.valueField]);
14451 this.value = value.join(',');
14453 if(this.hiddenField){
14454 this.hiddenField.dom.value = this.value;
14457 this.store.fireEvent("datachanged", this.store);
14462 clearItem : function()
14464 if(!this.multiple){
14470 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14478 if(this.tickable && !Roo.isTouch){
14479 this.view.refresh();
14483 inputEl: function ()
14485 if(Roo.isIOS && this.useNativeIOS){
14486 return this.el.select('select.roo-ios-select', true).first();
14489 if(Roo.isTouch && this.mobileTouchView){
14490 return this.el.select('input.form-control',true).first();
14494 return this.searchField;
14497 return this.el.select('input.form-control',true).first();
14500 onTickableFooterButtonClick : function(e, btn, el)
14502 e.preventDefault();
14504 this.lastItem = Roo.apply([], this.item);
14506 if(btn && btn.name == 'cancel'){
14507 this.tickItems = Roo.apply([], this.item);
14516 Roo.each(this.tickItems, function(o){
14524 validate : function()
14526 var v = this.getRawValue();
14529 v = this.getValue();
14532 if(this.disabled || this.allowBlank || v.length){
14537 this.markInvalid();
14541 tickableInputEl : function()
14543 if(!this.tickable || !this.editable){
14544 return this.inputEl();
14547 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14551 getAutoCreateTouchView : function()
14556 cls: 'form-group' //input-group
14562 type : this.inputType,
14563 cls : 'form-control x-combo-noedit',
14564 autocomplete: 'new-password',
14565 placeholder : this.placeholder || '',
14570 input.name = this.name;
14574 input.cls += ' input-' + this.size;
14577 if (this.disabled) {
14578 input.disabled = true;
14589 inputblock.cls += ' input-group';
14591 inputblock.cn.unshift({
14593 cls : 'input-group-addon',
14598 if(this.removable && !this.multiple){
14599 inputblock.cls += ' roo-removable';
14601 inputblock.cn.push({
14604 cls : 'roo-combo-removable-btn close'
14608 if(this.hasFeedback && !this.allowBlank){
14610 inputblock.cls += ' has-feedback';
14612 inputblock.cn.push({
14614 cls: 'glyphicon form-control-feedback'
14621 inputblock.cls += (this.before) ? '' : ' input-group';
14623 inputblock.cn.push({
14625 cls : 'input-group-addon',
14636 cls: 'form-hidden-field'
14650 cls: 'form-hidden-field'
14654 cls: 'roo-select2-choices',
14658 cls: 'roo-select2-search-field',
14671 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14677 if(!this.multiple && this.showToggleBtn){
14684 if (this.caret != false) {
14687 cls: 'fa fa-' + this.caret
14694 cls : 'input-group-addon btn dropdown-toggle',
14699 cls: 'combobox-clear',
14713 combobox.cls += ' roo-select2-container-multi';
14716 var align = this.labelAlign || this.parentLabelAlign();
14718 if (align ==='left' && this.fieldLabel.length) {
14723 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14724 tooltip : 'This field is required'
14728 cls : 'control-label',
14729 html : this.fieldLabel
14740 var labelCfg = cfg.cn[1];
14741 var contentCfg = cfg.cn[2];
14744 if(this.indicatorpos == 'right'){
14749 cls : 'control-label',
14753 html : this.fieldLabel
14757 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14758 tooltip : 'This field is required'
14771 labelCfg = cfg.cn[0];
14772 contentCfg = cfg.cn[1];
14777 if(this.labelWidth > 12){
14778 labelCfg.style = "width: " + this.labelWidth + 'px';
14781 if(this.labelWidth < 13 && this.labelmd == 0){
14782 this.labelmd = this.labelWidth;
14785 if(this.labellg > 0){
14786 labelCfg.cls += ' col-lg-' + this.labellg;
14787 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14790 if(this.labelmd > 0){
14791 labelCfg.cls += ' col-md-' + this.labelmd;
14792 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14795 if(this.labelsm > 0){
14796 labelCfg.cls += ' col-sm-' + this.labelsm;
14797 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14800 if(this.labelxs > 0){
14801 labelCfg.cls += ' col-xs-' + this.labelxs;
14802 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14806 } else if ( this.fieldLabel.length) {
14810 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14811 tooltip : 'This field is required'
14815 cls : 'control-label',
14816 html : this.fieldLabel
14827 if(this.indicatorpos == 'right'){
14831 cls : 'control-label',
14832 html : this.fieldLabel,
14836 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14837 tooltip : 'This field is required'
14854 var settings = this;
14856 ['xs','sm','md','lg'].map(function(size){
14857 if (settings[size]) {
14858 cfg.cls += ' col-' + size + '-' + settings[size];
14865 initTouchView : function()
14867 this.renderTouchView();
14869 this.touchViewEl.on('scroll', function(){
14870 this.el.dom.scrollTop = 0;
14873 this.originalValue = this.getValue();
14875 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14877 this.inputEl().on("click", this.showTouchView, this);
14878 if (this.triggerEl) {
14879 this.triggerEl.on("click", this.showTouchView, this);
14883 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14884 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14886 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14888 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14889 this.store.on('load', this.onTouchViewLoad, this);
14890 this.store.on('loadexception', this.onTouchViewLoadException, this);
14892 if(this.hiddenName){
14894 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14896 this.hiddenField.dom.value =
14897 this.hiddenValue !== undefined ? this.hiddenValue :
14898 this.value !== undefined ? this.value : '';
14900 this.el.dom.removeAttribute('name');
14901 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14905 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14906 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14909 if(this.removable && !this.multiple){
14910 var close = this.closeTriggerEl();
14912 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14913 close.on('click', this.removeBtnClick, this, close);
14917 * fix the bug in Safari iOS8
14919 this.inputEl().on("focus", function(e){
14920 document.activeElement.blur();
14928 renderTouchView : function()
14930 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14931 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14933 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14934 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14936 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14937 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14938 this.touchViewBodyEl.setStyle('overflow', 'auto');
14940 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14941 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14943 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14944 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14948 showTouchView : function()
14954 this.touchViewHeaderEl.hide();
14956 if(this.modalTitle.length){
14957 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14958 this.touchViewHeaderEl.show();
14961 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14962 this.touchViewEl.show();
14964 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14966 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14967 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14969 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14971 if(this.modalTitle.length){
14972 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14975 this.touchViewBodyEl.setHeight(bodyHeight);
14979 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14981 this.touchViewEl.addClass('in');
14984 this.doTouchViewQuery();
14988 hideTouchView : function()
14990 this.touchViewEl.removeClass('in');
14994 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14996 this.touchViewEl.setStyle('display', 'none');
15001 setTouchViewValue : function()
15008 Roo.each(this.tickItems, function(o){
15013 this.hideTouchView();
15016 doTouchViewQuery : function()
15025 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15029 if(!this.alwaysQuery || this.mode == 'local'){
15030 this.onTouchViewLoad();
15037 onTouchViewBeforeLoad : function(combo,opts)
15043 onTouchViewLoad : function()
15045 if(this.store.getCount() < 1){
15046 this.onTouchViewEmptyResults();
15050 this.clearTouchView();
15052 var rawValue = this.getRawValue();
15054 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15056 this.tickItems = [];
15058 this.store.data.each(function(d, rowIndex){
15059 var row = this.touchViewListGroup.createChild(template);
15061 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15062 row.addClass(d.data.cls);
15065 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15068 html : d.data[this.displayField]
15071 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15072 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15075 row.removeClass('selected');
15076 if(!this.multiple && this.valueField &&
15077 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15080 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15081 row.addClass('selected');
15084 if(this.multiple && this.valueField &&
15085 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15089 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15090 this.tickItems.push(d.data);
15093 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15097 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15099 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15101 if(this.modalTitle.length){
15102 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15105 var listHeight = this.touchViewListGroup.getHeight();
15109 if(firstChecked && listHeight > bodyHeight){
15110 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15115 onTouchViewLoadException : function()
15117 this.hideTouchView();
15120 onTouchViewEmptyResults : function()
15122 this.clearTouchView();
15124 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15126 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15130 clearTouchView : function()
15132 this.touchViewListGroup.dom.innerHTML = '';
15135 onTouchViewClick : function(e, el, o)
15137 e.preventDefault();
15140 var rowIndex = o.rowIndex;
15142 var r = this.store.getAt(rowIndex);
15144 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15146 if(!this.multiple){
15147 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15148 c.dom.removeAttribute('checked');
15151 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15153 this.setFromData(r.data);
15155 var close = this.closeTriggerEl();
15161 this.hideTouchView();
15163 this.fireEvent('select', this, r, rowIndex);
15168 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15169 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15170 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15174 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15175 this.addItem(r.data);
15176 this.tickItems.push(r.data);
15180 getAutoCreateNativeIOS : function()
15183 cls: 'form-group' //input-group,
15188 cls : 'roo-ios-select'
15192 combobox.name = this.name;
15195 if (this.disabled) {
15196 combobox.disabled = true;
15199 var settings = this;
15201 ['xs','sm','md','lg'].map(function(size){
15202 if (settings[size]) {
15203 cfg.cls += ' col-' + size + '-' + settings[size];
15213 initIOSView : function()
15215 this.store.on('load', this.onIOSViewLoad, this);
15220 onIOSViewLoad : function()
15222 if(this.store.getCount() < 1){
15226 this.clearIOSView();
15228 if(this.allowBlank) {
15230 var default_text = '-- SELECT --';
15232 var opt = this.inputEl().createChild({
15235 html : default_text
15239 o[this.valueField] = 0;
15240 o[this.displayField] = default_text;
15242 this.ios_options.push({
15249 this.store.data.each(function(d, rowIndex){
15253 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15254 html = d.data[this.displayField];
15259 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15260 value = d.data[this.valueField];
15269 if(this.value == d.data[this.valueField]){
15270 option['selected'] = true;
15273 var opt = this.inputEl().createChild(option);
15275 this.ios_options.push({
15282 this.inputEl().on('change', function(){
15283 this.fireEvent('select', this);
15288 clearIOSView: function()
15290 this.inputEl().dom.innerHTML = '';
15292 this.ios_options = [];
15295 setIOSValue: function(v)
15299 if(!this.ios_options){
15303 Roo.each(this.ios_options, function(opts){
15305 opts.el.dom.removeAttribute('selected');
15307 if(opts.data[this.valueField] != v){
15311 opts.el.dom.setAttribute('selected', true);
15317 * @cfg {Boolean} grow
15321 * @cfg {Number} growMin
15325 * @cfg {Number} growMax
15334 Roo.apply(Roo.bootstrap.ComboBox, {
15338 cls: 'modal-header',
15360 cls: 'list-group-item',
15364 cls: 'roo-combobox-list-group-item-value'
15368 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15382 listItemCheckbox : {
15384 cls: 'list-group-item',
15388 cls: 'roo-combobox-list-group-item-value'
15392 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15408 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15413 cls: 'modal-footer',
15421 cls: 'col-xs-6 text-left',
15424 cls: 'btn btn-danger roo-touch-view-cancel',
15430 cls: 'col-xs-6 text-right',
15433 cls: 'btn btn-success roo-touch-view-ok',
15444 Roo.apply(Roo.bootstrap.ComboBox, {
15446 touchViewTemplate : {
15448 cls: 'modal fade roo-combobox-touch-view',
15452 cls: 'modal-dialog',
15453 style : 'position:fixed', // we have to fix position....
15457 cls: 'modal-content',
15459 Roo.bootstrap.ComboBox.header,
15460 Roo.bootstrap.ComboBox.body,
15461 Roo.bootstrap.ComboBox.footer
15470 * Ext JS Library 1.1.1
15471 * Copyright(c) 2006-2007, Ext JS, LLC.
15473 * Originally Released Under LGPL - original licence link has changed is not relivant.
15476 * <script type="text/javascript">
15481 * @extends Roo.util.Observable
15482 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15483 * This class also supports single and multi selection modes. <br>
15484 * Create a data model bound view:
15486 var store = new Roo.data.Store(...);
15488 var view = new Roo.View({
15490 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15492 singleSelect: true,
15493 selectedClass: "ydataview-selected",
15497 // listen for node click?
15498 view.on("click", function(vw, index, node, e){
15499 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15503 dataModel.load("foobar.xml");
15505 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15507 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15508 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15510 * Note: old style constructor is still suported (container, template, config)
15513 * Create a new View
15514 * @param {Object} config The config object
15517 Roo.View = function(config, depreciated_tpl, depreciated_config){
15519 this.parent = false;
15521 if (typeof(depreciated_tpl) == 'undefined') {
15522 // new way.. - universal constructor.
15523 Roo.apply(this, config);
15524 this.el = Roo.get(this.el);
15527 this.el = Roo.get(config);
15528 this.tpl = depreciated_tpl;
15529 Roo.apply(this, depreciated_config);
15531 this.wrapEl = this.el.wrap().wrap();
15532 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15535 if(typeof(this.tpl) == "string"){
15536 this.tpl = new Roo.Template(this.tpl);
15538 // support xtype ctors..
15539 this.tpl = new Roo.factory(this.tpl, Roo);
15543 this.tpl.compile();
15548 * @event beforeclick
15549 * Fires before a click is processed. Returns false to cancel the default action.
15550 * @param {Roo.View} this
15551 * @param {Number} index The index of the target node
15552 * @param {HTMLElement} node The target node
15553 * @param {Roo.EventObject} e The raw event object
15555 "beforeclick" : true,
15558 * Fires when a template node is clicked.
15559 * @param {Roo.View} this
15560 * @param {Number} index The index of the target node
15561 * @param {HTMLElement} node The target node
15562 * @param {Roo.EventObject} e The raw event object
15567 * Fires when a template node is double clicked.
15568 * @param {Roo.View} this
15569 * @param {Number} index The index of the target node
15570 * @param {HTMLElement} node The target node
15571 * @param {Roo.EventObject} e The raw event object
15575 * @event contextmenu
15576 * Fires when a template node is right clicked.
15577 * @param {Roo.View} this
15578 * @param {Number} index The index of the target node
15579 * @param {HTMLElement} node The target node
15580 * @param {Roo.EventObject} e The raw event object
15582 "contextmenu" : true,
15584 * @event selectionchange
15585 * Fires when the selected nodes change.
15586 * @param {Roo.View} this
15587 * @param {Array} selections Array of the selected nodes
15589 "selectionchange" : true,
15592 * @event beforeselect
15593 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15594 * @param {Roo.View} this
15595 * @param {HTMLElement} node The node to be selected
15596 * @param {Array} selections Array of currently selected nodes
15598 "beforeselect" : true,
15600 * @event preparedata
15601 * Fires on every row to render, to allow you to change the data.
15602 * @param {Roo.View} this
15603 * @param {Object} data to be rendered (change this)
15605 "preparedata" : true
15613 "click": this.onClick,
15614 "dblclick": this.onDblClick,
15615 "contextmenu": this.onContextMenu,
15619 this.selections = [];
15621 this.cmp = new Roo.CompositeElementLite([]);
15623 this.store = Roo.factory(this.store, Roo.data);
15624 this.setStore(this.store, true);
15627 if ( this.footer && this.footer.xtype) {
15629 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15631 this.footer.dataSource = this.store;
15632 this.footer.container = fctr;
15633 this.footer = Roo.factory(this.footer, Roo);
15634 fctr.insertFirst(this.el);
15636 // this is a bit insane - as the paging toolbar seems to detach the el..
15637 // dom.parentNode.parentNode.parentNode
15638 // they get detached?
15642 Roo.View.superclass.constructor.call(this);
15647 Roo.extend(Roo.View, Roo.util.Observable, {
15650 * @cfg {Roo.data.Store} store Data store to load data from.
15655 * @cfg {String|Roo.Element} el The container element.
15660 * @cfg {String|Roo.Template} tpl The template used by this View
15664 * @cfg {String} dataName the named area of the template to use as the data area
15665 * Works with domtemplates roo-name="name"
15669 * @cfg {String} selectedClass The css class to add to selected nodes
15671 selectedClass : "x-view-selected",
15673 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15678 * @cfg {String} text to display on mask (default Loading)
15682 * @cfg {Boolean} multiSelect Allow multiple selection
15684 multiSelect : false,
15686 * @cfg {Boolean} singleSelect Allow single selection
15688 singleSelect: false,
15691 * @cfg {Boolean} toggleSelect - selecting
15693 toggleSelect : false,
15696 * @cfg {Boolean} tickable - selecting
15701 * Returns the element this view is bound to.
15702 * @return {Roo.Element}
15704 getEl : function(){
15705 return this.wrapEl;
15711 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15713 refresh : function(){
15714 //Roo.log('refresh');
15717 // if we are using something like 'domtemplate', then
15718 // the what gets used is:
15719 // t.applySubtemplate(NAME, data, wrapping data..)
15720 // the outer template then get' applied with
15721 // the store 'extra data'
15722 // and the body get's added to the
15723 // roo-name="data" node?
15724 // <span class='roo-tpl-{name}'></span> ?????
15728 this.clearSelections();
15729 this.el.update("");
15731 var records = this.store.getRange();
15732 if(records.length < 1) {
15734 // is this valid?? = should it render a template??
15736 this.el.update(this.emptyText);
15740 if (this.dataName) {
15741 this.el.update(t.apply(this.store.meta)); //????
15742 el = this.el.child('.roo-tpl-' + this.dataName);
15745 for(var i = 0, len = records.length; i < len; i++){
15746 var data = this.prepareData(records[i].data, i, records[i]);
15747 this.fireEvent("preparedata", this, data, i, records[i]);
15749 var d = Roo.apply({}, data);
15752 Roo.apply(d, {'roo-id' : Roo.id()});
15756 Roo.each(this.parent.item, function(item){
15757 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15760 Roo.apply(d, {'roo-data-checked' : 'checked'});
15764 html[html.length] = Roo.util.Format.trim(
15766 t.applySubtemplate(this.dataName, d, this.store.meta) :
15773 el.update(html.join(""));
15774 this.nodes = el.dom.childNodes;
15775 this.updateIndexes(0);
15780 * Function to override to reformat the data that is sent to
15781 * the template for each node.
15782 * DEPRICATED - use the preparedata event handler.
15783 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15784 * a JSON object for an UpdateManager bound view).
15786 prepareData : function(data, index, record)
15788 this.fireEvent("preparedata", this, data, index, record);
15792 onUpdate : function(ds, record){
15793 // Roo.log('on update');
15794 this.clearSelections();
15795 var index = this.store.indexOf(record);
15796 var n = this.nodes[index];
15797 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15798 n.parentNode.removeChild(n);
15799 this.updateIndexes(index, index);
15805 onAdd : function(ds, records, index)
15807 //Roo.log(['on Add', ds, records, index] );
15808 this.clearSelections();
15809 if(this.nodes.length == 0){
15813 var n = this.nodes[index];
15814 for(var i = 0, len = records.length; i < len; i++){
15815 var d = this.prepareData(records[i].data, i, records[i]);
15817 this.tpl.insertBefore(n, d);
15820 this.tpl.append(this.el, d);
15823 this.updateIndexes(index);
15826 onRemove : function(ds, record, index){
15827 // Roo.log('onRemove');
15828 this.clearSelections();
15829 var el = this.dataName ?
15830 this.el.child('.roo-tpl-' + this.dataName) :
15833 el.dom.removeChild(this.nodes[index]);
15834 this.updateIndexes(index);
15838 * Refresh an individual node.
15839 * @param {Number} index
15841 refreshNode : function(index){
15842 this.onUpdate(this.store, this.store.getAt(index));
15845 updateIndexes : function(startIndex, endIndex){
15846 var ns = this.nodes;
15847 startIndex = startIndex || 0;
15848 endIndex = endIndex || ns.length - 1;
15849 for(var i = startIndex; i <= endIndex; i++){
15850 ns[i].nodeIndex = i;
15855 * Changes the data store this view uses and refresh the view.
15856 * @param {Store} store
15858 setStore : function(store, initial){
15859 if(!initial && this.store){
15860 this.store.un("datachanged", this.refresh);
15861 this.store.un("add", this.onAdd);
15862 this.store.un("remove", this.onRemove);
15863 this.store.un("update", this.onUpdate);
15864 this.store.un("clear", this.refresh);
15865 this.store.un("beforeload", this.onBeforeLoad);
15866 this.store.un("load", this.onLoad);
15867 this.store.un("loadexception", this.onLoad);
15871 store.on("datachanged", this.refresh, this);
15872 store.on("add", this.onAdd, this);
15873 store.on("remove", this.onRemove, this);
15874 store.on("update", this.onUpdate, this);
15875 store.on("clear", this.refresh, this);
15876 store.on("beforeload", this.onBeforeLoad, this);
15877 store.on("load", this.onLoad, this);
15878 store.on("loadexception", this.onLoad, this);
15886 * onbeforeLoad - masks the loading area.
15889 onBeforeLoad : function(store,opts)
15891 //Roo.log('onBeforeLoad');
15893 this.el.update("");
15895 this.el.mask(this.mask ? this.mask : "Loading" );
15897 onLoad : function ()
15904 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15905 * @param {HTMLElement} node
15906 * @return {HTMLElement} The template node
15908 findItemFromChild : function(node){
15909 var el = this.dataName ?
15910 this.el.child('.roo-tpl-' + this.dataName,true) :
15913 if(!node || node.parentNode == el){
15916 var p = node.parentNode;
15917 while(p && p != el){
15918 if(p.parentNode == el){
15927 onClick : function(e){
15928 var item = this.findItemFromChild(e.getTarget());
15930 var index = this.indexOf(item);
15931 if(this.onItemClick(item, index, e) !== false){
15932 this.fireEvent("click", this, index, item, e);
15935 this.clearSelections();
15940 onContextMenu : function(e){
15941 var item = this.findItemFromChild(e.getTarget());
15943 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15948 onDblClick : function(e){
15949 var item = this.findItemFromChild(e.getTarget());
15951 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15955 onItemClick : function(item, index, e)
15957 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15960 if (this.toggleSelect) {
15961 var m = this.isSelected(item) ? 'unselect' : 'select';
15964 _t[m](item, true, false);
15967 if(this.multiSelect || this.singleSelect){
15968 if(this.multiSelect && e.shiftKey && this.lastSelection){
15969 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15971 this.select(item, this.multiSelect && e.ctrlKey);
15972 this.lastSelection = item;
15975 if(!this.tickable){
15976 e.preventDefault();
15984 * Get the number of selected nodes.
15987 getSelectionCount : function(){
15988 return this.selections.length;
15992 * Get the currently selected nodes.
15993 * @return {Array} An array of HTMLElements
15995 getSelectedNodes : function(){
15996 return this.selections;
16000 * Get the indexes of the selected nodes.
16003 getSelectedIndexes : function(){
16004 var indexes = [], s = this.selections;
16005 for(var i = 0, len = s.length; i < len; i++){
16006 indexes.push(s[i].nodeIndex);
16012 * Clear all selections
16013 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16015 clearSelections : function(suppressEvent){
16016 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16017 this.cmp.elements = this.selections;
16018 this.cmp.removeClass(this.selectedClass);
16019 this.selections = [];
16020 if(!suppressEvent){
16021 this.fireEvent("selectionchange", this, this.selections);
16027 * Returns true if the passed node is selected
16028 * @param {HTMLElement/Number} node The node or node index
16029 * @return {Boolean}
16031 isSelected : function(node){
16032 var s = this.selections;
16036 node = this.getNode(node);
16037 return s.indexOf(node) !== -1;
16042 * @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
16043 * @param {Boolean} keepExisting (optional) true to keep existing selections
16044 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16046 select : function(nodeInfo, keepExisting, suppressEvent){
16047 if(nodeInfo instanceof Array){
16049 this.clearSelections(true);
16051 for(var i = 0, len = nodeInfo.length; i < len; i++){
16052 this.select(nodeInfo[i], true, true);
16056 var node = this.getNode(nodeInfo);
16057 if(!node || this.isSelected(node)){
16058 return; // already selected.
16061 this.clearSelections(true);
16064 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16065 Roo.fly(node).addClass(this.selectedClass);
16066 this.selections.push(node);
16067 if(!suppressEvent){
16068 this.fireEvent("selectionchange", this, this.selections);
16076 * @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
16077 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16078 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16080 unselect : function(nodeInfo, keepExisting, suppressEvent)
16082 if(nodeInfo instanceof Array){
16083 Roo.each(this.selections, function(s) {
16084 this.unselect(s, nodeInfo);
16088 var node = this.getNode(nodeInfo);
16089 if(!node || !this.isSelected(node)){
16090 //Roo.log("not selected");
16091 return; // not selected.
16095 Roo.each(this.selections, function(s) {
16097 Roo.fly(node).removeClass(this.selectedClass);
16104 this.selections= ns;
16105 this.fireEvent("selectionchange", this, this.selections);
16109 * Gets a template node.
16110 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16111 * @return {HTMLElement} The node or null if it wasn't found
16113 getNode : function(nodeInfo){
16114 if(typeof nodeInfo == "string"){
16115 return document.getElementById(nodeInfo);
16116 }else if(typeof nodeInfo == "number"){
16117 return this.nodes[nodeInfo];
16123 * Gets a range template nodes.
16124 * @param {Number} startIndex
16125 * @param {Number} endIndex
16126 * @return {Array} An array of nodes
16128 getNodes : function(start, end){
16129 var ns = this.nodes;
16130 start = start || 0;
16131 end = typeof end == "undefined" ? ns.length - 1 : end;
16134 for(var i = start; i <= end; i++){
16138 for(var i = start; i >= end; i--){
16146 * Finds the index of the passed node
16147 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16148 * @return {Number} The index of the node or -1
16150 indexOf : function(node){
16151 node = this.getNode(node);
16152 if(typeof node.nodeIndex == "number"){
16153 return node.nodeIndex;
16155 var ns = this.nodes;
16156 for(var i = 0, len = ns.length; i < len; i++){
16167 * based on jquery fullcalendar
16171 Roo.bootstrap = Roo.bootstrap || {};
16173 * @class Roo.bootstrap.Calendar
16174 * @extends Roo.bootstrap.Component
16175 * Bootstrap Calendar class
16176 * @cfg {Boolean} loadMask (true|false) default false
16177 * @cfg {Object} header generate the user specific header of the calendar, default false
16180 * Create a new Container
16181 * @param {Object} config The config object
16186 Roo.bootstrap.Calendar = function(config){
16187 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16191 * Fires when a date is selected
16192 * @param {DatePicker} this
16193 * @param {Date} date The selected date
16197 * @event monthchange
16198 * Fires when the displayed month changes
16199 * @param {DatePicker} this
16200 * @param {Date} date The selected month
16202 'monthchange': true,
16204 * @event evententer
16205 * Fires when mouse over an event
16206 * @param {Calendar} this
16207 * @param {event} Event
16209 'evententer': true,
16211 * @event eventleave
16212 * Fires when the mouse leaves an
16213 * @param {Calendar} this
16216 'eventleave': true,
16218 * @event eventclick
16219 * Fires when the mouse click an
16220 * @param {Calendar} this
16229 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16232 * @cfg {Number} startDay
16233 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16241 getAutoCreate : function(){
16244 var fc_button = function(name, corner, style, content ) {
16245 return Roo.apply({},{
16247 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16249 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16252 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16263 style : 'width:100%',
16270 cls : 'fc-header-left',
16272 fc_button('prev', 'left', 'arrow', '‹' ),
16273 fc_button('next', 'right', 'arrow', '›' ),
16274 { tag: 'span', cls: 'fc-header-space' },
16275 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16283 cls : 'fc-header-center',
16287 cls: 'fc-header-title',
16290 html : 'month / year'
16298 cls : 'fc-header-right',
16300 /* fc_button('month', 'left', '', 'month' ),
16301 fc_button('week', '', '', 'week' ),
16302 fc_button('day', 'right', '', 'day' )
16314 header = this.header;
16317 var cal_heads = function() {
16319 // fixme - handle this.
16321 for (var i =0; i < Date.dayNames.length; i++) {
16322 var d = Date.dayNames[i];
16325 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16326 html : d.substring(0,3)
16330 ret[0].cls += ' fc-first';
16331 ret[6].cls += ' fc-last';
16334 var cal_cell = function(n) {
16337 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16342 cls: 'fc-day-number',
16346 cls: 'fc-day-content',
16350 style: 'position: relative;' // height: 17px;
16362 var cal_rows = function() {
16365 for (var r = 0; r < 6; r++) {
16372 for (var i =0; i < Date.dayNames.length; i++) {
16373 var d = Date.dayNames[i];
16374 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16377 row.cn[0].cls+=' fc-first';
16378 row.cn[0].cn[0].style = 'min-height:90px';
16379 row.cn[6].cls+=' fc-last';
16383 ret[0].cls += ' fc-first';
16384 ret[4].cls += ' fc-prev-last';
16385 ret[5].cls += ' fc-last';
16392 cls: 'fc-border-separate',
16393 style : 'width:100%',
16401 cls : 'fc-first fc-last',
16419 cls : 'fc-content',
16420 style : "position: relative;",
16423 cls : 'fc-view fc-view-month fc-grid',
16424 style : 'position: relative',
16425 unselectable : 'on',
16428 cls : 'fc-event-container',
16429 style : 'position:absolute;z-index:8;top:0;left:0;'
16447 initEvents : function()
16450 throw "can not find store for calendar";
16456 style: "text-align:center",
16460 style: "background-color:white;width:50%;margin:250 auto",
16464 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16475 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16477 var size = this.el.select('.fc-content', true).first().getSize();
16478 this.maskEl.setSize(size.width, size.height);
16479 this.maskEl.enableDisplayMode("block");
16480 if(!this.loadMask){
16481 this.maskEl.hide();
16484 this.store = Roo.factory(this.store, Roo.data);
16485 this.store.on('load', this.onLoad, this);
16486 this.store.on('beforeload', this.onBeforeLoad, this);
16490 this.cells = this.el.select('.fc-day',true);
16491 //Roo.log(this.cells);
16492 this.textNodes = this.el.query('.fc-day-number');
16493 this.cells.addClassOnOver('fc-state-hover');
16495 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16496 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16497 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16498 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16500 this.on('monthchange', this.onMonthChange, this);
16502 this.update(new Date().clearTime());
16505 resize : function() {
16506 var sz = this.el.getSize();
16508 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16509 this.el.select('.fc-day-content div',true).setHeight(34);
16514 showPrevMonth : function(e){
16515 this.update(this.activeDate.add("mo", -1));
16517 showToday : function(e){
16518 this.update(new Date().clearTime());
16521 showNextMonth : function(e){
16522 this.update(this.activeDate.add("mo", 1));
16526 showPrevYear : function(){
16527 this.update(this.activeDate.add("y", -1));
16531 showNextYear : function(){
16532 this.update(this.activeDate.add("y", 1));
16537 update : function(date)
16539 var vd = this.activeDate;
16540 this.activeDate = date;
16541 // if(vd && this.el){
16542 // var t = date.getTime();
16543 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16544 // Roo.log('using add remove');
16546 // this.fireEvent('monthchange', this, date);
16548 // this.cells.removeClass("fc-state-highlight");
16549 // this.cells.each(function(c){
16550 // if(c.dateValue == t){
16551 // c.addClass("fc-state-highlight");
16552 // setTimeout(function(){
16553 // try{c.dom.firstChild.focus();}catch(e){}
16563 var days = date.getDaysInMonth();
16565 var firstOfMonth = date.getFirstDateOfMonth();
16566 var startingPos = firstOfMonth.getDay()-this.startDay;
16568 if(startingPos < this.startDay){
16572 var pm = date.add(Date.MONTH, -1);
16573 var prevStart = pm.getDaysInMonth()-startingPos;
16575 this.cells = this.el.select('.fc-day',true);
16576 this.textNodes = this.el.query('.fc-day-number');
16577 this.cells.addClassOnOver('fc-state-hover');
16579 var cells = this.cells.elements;
16580 var textEls = this.textNodes;
16582 Roo.each(cells, function(cell){
16583 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16586 days += startingPos;
16588 // convert everything to numbers so it's fast
16589 var day = 86400000;
16590 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16593 //Roo.log(prevStart);
16595 var today = new Date().clearTime().getTime();
16596 var sel = date.clearTime().getTime();
16597 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16598 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16599 var ddMatch = this.disabledDatesRE;
16600 var ddText = this.disabledDatesText;
16601 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16602 var ddaysText = this.disabledDaysText;
16603 var format = this.format;
16605 var setCellClass = function(cal, cell){
16609 //Roo.log('set Cell Class');
16611 var t = d.getTime();
16615 cell.dateValue = t;
16617 cell.className += " fc-today";
16618 cell.className += " fc-state-highlight";
16619 cell.title = cal.todayText;
16622 // disable highlight in other month..
16623 //cell.className += " fc-state-highlight";
16628 cell.className = " fc-state-disabled";
16629 cell.title = cal.minText;
16633 cell.className = " fc-state-disabled";
16634 cell.title = cal.maxText;
16638 if(ddays.indexOf(d.getDay()) != -1){
16639 cell.title = ddaysText;
16640 cell.className = " fc-state-disabled";
16643 if(ddMatch && format){
16644 var fvalue = d.dateFormat(format);
16645 if(ddMatch.test(fvalue)){
16646 cell.title = ddText.replace("%0", fvalue);
16647 cell.className = " fc-state-disabled";
16651 if (!cell.initialClassName) {
16652 cell.initialClassName = cell.dom.className;
16655 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16660 for(; i < startingPos; i++) {
16661 textEls[i].innerHTML = (++prevStart);
16662 d.setDate(d.getDate()+1);
16664 cells[i].className = "fc-past fc-other-month";
16665 setCellClass(this, cells[i]);
16670 for(; i < days; i++){
16671 intDay = i - startingPos + 1;
16672 textEls[i].innerHTML = (intDay);
16673 d.setDate(d.getDate()+1);
16675 cells[i].className = ''; // "x-date-active";
16676 setCellClass(this, cells[i]);
16680 for(; i < 42; i++) {
16681 textEls[i].innerHTML = (++extraDays);
16682 d.setDate(d.getDate()+1);
16684 cells[i].className = "fc-future fc-other-month";
16685 setCellClass(this, cells[i]);
16688 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16690 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16692 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16693 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16695 if(totalRows != 6){
16696 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16697 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16700 this.fireEvent('monthchange', this, date);
16704 if(!this.internalRender){
16705 var main = this.el.dom.firstChild;
16706 var w = main.offsetWidth;
16707 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16708 Roo.fly(main).setWidth(w);
16709 this.internalRender = true;
16710 // opera does not respect the auto grow header center column
16711 // then, after it gets a width opera refuses to recalculate
16712 // without a second pass
16713 if(Roo.isOpera && !this.secondPass){
16714 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16715 this.secondPass = true;
16716 this.update.defer(10, this, [date]);
16723 findCell : function(dt) {
16724 dt = dt.clearTime().getTime();
16726 this.cells.each(function(c){
16727 //Roo.log("check " +c.dateValue + '?=' + dt);
16728 if(c.dateValue == dt){
16738 findCells : function(ev) {
16739 var s = ev.start.clone().clearTime().getTime();
16741 var e= ev.end.clone().clearTime().getTime();
16744 this.cells.each(function(c){
16745 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16747 if(c.dateValue > e){
16750 if(c.dateValue < s){
16759 // findBestRow: function(cells)
16763 // for (var i =0 ; i < cells.length;i++) {
16764 // ret = Math.max(cells[i].rows || 0,ret);
16771 addItem : function(ev)
16773 // look for vertical location slot in
16774 var cells = this.findCells(ev);
16776 // ev.row = this.findBestRow(cells);
16778 // work out the location.
16782 for(var i =0; i < cells.length; i++) {
16784 cells[i].row = cells[0].row;
16787 cells[i].row = cells[i].row + 1;
16797 if (crow.start.getY() == cells[i].getY()) {
16799 crow.end = cells[i];
16816 cells[0].events.push(ev);
16818 this.calevents.push(ev);
16821 clearEvents: function() {
16823 if(!this.calevents){
16827 Roo.each(this.cells.elements, function(c){
16833 Roo.each(this.calevents, function(e) {
16834 Roo.each(e.els, function(el) {
16835 el.un('mouseenter' ,this.onEventEnter, this);
16836 el.un('mouseleave' ,this.onEventLeave, this);
16841 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16847 renderEvents: function()
16851 this.cells.each(function(c) {
16860 if(c.row != c.events.length){
16861 r = 4 - (4 - (c.row - c.events.length));
16864 c.events = ev.slice(0, r);
16865 c.more = ev.slice(r);
16867 if(c.more.length && c.more.length == 1){
16868 c.events.push(c.more.pop());
16871 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16875 this.cells.each(function(c) {
16877 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16880 for (var e = 0; e < c.events.length; e++){
16881 var ev = c.events[e];
16882 var rows = ev.rows;
16884 for(var i = 0; i < rows.length; i++) {
16886 // how many rows should it span..
16889 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16890 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16892 unselectable : "on",
16895 cls: 'fc-event-inner',
16899 // cls: 'fc-event-time',
16900 // html : cells.length > 1 ? '' : ev.time
16904 cls: 'fc-event-title',
16905 html : String.format('{0}', ev.title)
16912 cls: 'ui-resizable-handle ui-resizable-e',
16913 html : '  '
16920 cfg.cls += ' fc-event-start';
16922 if ((i+1) == rows.length) {
16923 cfg.cls += ' fc-event-end';
16926 var ctr = _this.el.select('.fc-event-container',true).first();
16927 var cg = ctr.createChild(cfg);
16929 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16930 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16932 var r = (c.more.length) ? 1 : 0;
16933 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16934 cg.setWidth(ebox.right - sbox.x -2);
16936 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16937 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16938 cg.on('click', _this.onEventClick, _this, ev);
16949 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16950 style : 'position: absolute',
16951 unselectable : "on",
16954 cls: 'fc-event-inner',
16958 cls: 'fc-event-title',
16966 cls: 'ui-resizable-handle ui-resizable-e',
16967 html : '  '
16973 var ctr = _this.el.select('.fc-event-container',true).first();
16974 var cg = ctr.createChild(cfg);
16976 var sbox = c.select('.fc-day-content',true).first().getBox();
16977 var ebox = c.select('.fc-day-content',true).first().getBox();
16979 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16980 cg.setWidth(ebox.right - sbox.x -2);
16982 cg.on('click', _this.onMoreEventClick, _this, c.more);
16992 onEventEnter: function (e, el,event,d) {
16993 this.fireEvent('evententer', this, el, event);
16996 onEventLeave: function (e, el,event,d) {
16997 this.fireEvent('eventleave', this, el, event);
17000 onEventClick: function (e, el,event,d) {
17001 this.fireEvent('eventclick', this, el, event);
17004 onMonthChange: function () {
17008 onMoreEventClick: function(e, el, more)
17012 this.calpopover.placement = 'right';
17013 this.calpopover.setTitle('More');
17015 this.calpopover.setContent('');
17017 var ctr = this.calpopover.el.select('.popover-content', true).first();
17019 Roo.each(more, function(m){
17021 cls : 'fc-event-hori fc-event-draggable',
17024 var cg = ctr.createChild(cfg);
17026 cg.on('click', _this.onEventClick, _this, m);
17029 this.calpopover.show(el);
17034 onLoad: function ()
17036 this.calevents = [];
17039 if(this.store.getCount() > 0){
17040 this.store.data.each(function(d){
17043 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17044 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17045 time : d.data.start_time,
17046 title : d.data.title,
17047 description : d.data.description,
17048 venue : d.data.venue
17053 this.renderEvents();
17055 if(this.calevents.length && this.loadMask){
17056 this.maskEl.hide();
17060 onBeforeLoad: function()
17062 this.clearEvents();
17064 this.maskEl.show();
17078 * @class Roo.bootstrap.Popover
17079 * @extends Roo.bootstrap.Component
17080 * Bootstrap Popover class
17081 * @cfg {String} html contents of the popover (or false to use children..)
17082 * @cfg {String} title of popover (or false to hide)
17083 * @cfg {String} placement how it is placed
17084 * @cfg {String} trigger click || hover (or false to trigger manually)
17085 * @cfg {String} over what (parent or false to trigger manually.)
17086 * @cfg {Number} delay - delay before showing
17089 * Create a new Popover
17090 * @param {Object} config The config object
17093 Roo.bootstrap.Popover = function(config){
17094 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17100 * After the popover show
17102 * @param {Roo.bootstrap.Popover} this
17107 * After the popover hide
17109 * @param {Roo.bootstrap.Popover} this
17115 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17117 title: 'Fill in a title',
17120 placement : 'right',
17121 trigger : 'hover', // hover
17127 can_build_overlaid : false,
17129 getChildContainer : function()
17131 return this.el.select('.popover-content',true).first();
17134 getAutoCreate : function(){
17137 cls : 'popover roo-dynamic',
17138 style: 'display:block',
17144 cls : 'popover-inner',
17148 cls: 'popover-title',
17152 cls : 'popover-content',
17163 setTitle: function(str)
17166 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17168 setContent: function(str)
17171 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17173 // as it get's added to the bottom of the page.
17174 onRender : function(ct, position)
17176 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17178 var cfg = Roo.apply({}, this.getAutoCreate());
17182 cfg.cls += ' ' + this.cls;
17185 cfg.style = this.style;
17187 //Roo.log("adding to ");
17188 this.el = Roo.get(document.body).createChild(cfg, position);
17189 // Roo.log(this.el);
17194 initEvents : function()
17196 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17197 this.el.enableDisplayMode('block');
17199 if (this.over === false) {
17202 if (this.triggers === false) {
17205 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17206 var triggers = this.trigger ? this.trigger.split(' ') : [];
17207 Roo.each(triggers, function(trigger) {
17209 if (trigger == 'click') {
17210 on_el.on('click', this.toggle, this);
17211 } else if (trigger != 'manual') {
17212 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17213 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17215 on_el.on(eventIn ,this.enter, this);
17216 on_el.on(eventOut, this.leave, this);
17227 toggle : function () {
17228 this.hoverState == 'in' ? this.leave() : this.enter();
17231 enter : function () {
17233 clearTimeout(this.timeout);
17235 this.hoverState = 'in';
17237 if (!this.delay || !this.delay.show) {
17242 this.timeout = setTimeout(function () {
17243 if (_t.hoverState == 'in') {
17246 }, this.delay.show)
17249 leave : function() {
17250 clearTimeout(this.timeout);
17252 this.hoverState = 'out';
17254 if (!this.delay || !this.delay.hide) {
17259 this.timeout = setTimeout(function () {
17260 if (_t.hoverState == 'out') {
17263 }, this.delay.hide)
17266 show : function (on_el)
17269 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17273 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17274 if (this.html !== false) {
17275 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17277 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17278 if (!this.title.length) {
17279 this.el.select('.popover-title',true).hide();
17282 var placement = typeof this.placement == 'function' ?
17283 this.placement.call(this, this.el, on_el) :
17286 var autoToken = /\s?auto?\s?/i;
17287 var autoPlace = autoToken.test(placement);
17289 placement = placement.replace(autoToken, '') || 'top';
17293 //this.el.setXY([0,0]);
17295 this.el.dom.style.display='block';
17296 this.el.addClass(placement);
17298 //this.el.appendTo(on_el);
17300 var p = this.getPosition();
17301 var box = this.el.getBox();
17306 var align = Roo.bootstrap.Popover.alignment[placement];
17307 this.el.alignTo(on_el, align[0],align[1]);
17308 //var arrow = this.el.select('.arrow',true).first();
17309 //arrow.set(align[2],
17311 this.el.addClass('in');
17314 if (this.el.hasClass('fade')) {
17318 this.hoverState = 'in';
17320 this.fireEvent('show', this);
17325 this.el.setXY([0,0]);
17326 this.el.removeClass('in');
17328 this.hoverState = null;
17330 this.fireEvent('hide', this);
17335 Roo.bootstrap.Popover.alignment = {
17336 'left' : ['r-l', [-10,0], 'right'],
17337 'right' : ['l-r', [10,0], 'left'],
17338 'bottom' : ['t-b', [0,10], 'top'],
17339 'top' : [ 'b-t', [0,-10], 'bottom']
17350 * @class Roo.bootstrap.Progress
17351 * @extends Roo.bootstrap.Component
17352 * Bootstrap Progress class
17353 * @cfg {Boolean} striped striped of the progress bar
17354 * @cfg {Boolean} active animated of the progress bar
17358 * Create a new Progress
17359 * @param {Object} config The config object
17362 Roo.bootstrap.Progress = function(config){
17363 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17366 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17371 getAutoCreate : function(){
17379 cfg.cls += ' progress-striped';
17383 cfg.cls += ' active';
17402 * @class Roo.bootstrap.ProgressBar
17403 * @extends Roo.bootstrap.Component
17404 * Bootstrap ProgressBar class
17405 * @cfg {Number} aria_valuenow aria-value now
17406 * @cfg {Number} aria_valuemin aria-value min
17407 * @cfg {Number} aria_valuemax aria-value max
17408 * @cfg {String} label label for the progress bar
17409 * @cfg {String} panel (success | info | warning | danger )
17410 * @cfg {String} role role of the progress bar
17411 * @cfg {String} sr_only text
17415 * Create a new ProgressBar
17416 * @param {Object} config The config object
17419 Roo.bootstrap.ProgressBar = function(config){
17420 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17423 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17427 aria_valuemax : 100,
17433 getAutoCreate : function()
17438 cls: 'progress-bar',
17439 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17451 cfg.role = this.role;
17454 if(this.aria_valuenow){
17455 cfg['aria-valuenow'] = this.aria_valuenow;
17458 if(this.aria_valuemin){
17459 cfg['aria-valuemin'] = this.aria_valuemin;
17462 if(this.aria_valuemax){
17463 cfg['aria-valuemax'] = this.aria_valuemax;
17466 if(this.label && !this.sr_only){
17467 cfg.html = this.label;
17471 cfg.cls += ' progress-bar-' + this.panel;
17477 update : function(aria_valuenow)
17479 this.aria_valuenow = aria_valuenow;
17481 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17496 * @class Roo.bootstrap.TabGroup
17497 * @extends Roo.bootstrap.Column
17498 * Bootstrap Column class
17499 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17500 * @cfg {Boolean} carousel true to make the group behave like a carousel
17501 * @cfg {Boolean} bullets show bullets for the panels
17502 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17503 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17504 * @cfg {Boolean} showarrow (true|false) show arrow default true
17507 * Create a new TabGroup
17508 * @param {Object} config The config object
17511 Roo.bootstrap.TabGroup = function(config){
17512 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17514 this.navId = Roo.id();
17517 Roo.bootstrap.TabGroup.register(this);
17521 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17524 transition : false,
17529 slideOnTouch : false,
17532 getAutoCreate : function()
17534 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17536 cfg.cls += ' tab-content';
17538 if (this.carousel) {
17539 cfg.cls += ' carousel slide';
17542 cls : 'carousel-inner',
17546 if(this.bullets && !Roo.isTouch){
17549 cls : 'carousel-bullets',
17553 if(this.bullets_cls){
17554 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17561 cfg.cn[0].cn.push(bullets);
17564 if(this.showarrow){
17565 cfg.cn[0].cn.push({
17567 class : 'carousel-arrow',
17571 class : 'carousel-prev',
17575 class : 'fa fa-chevron-left'
17581 class : 'carousel-next',
17585 class : 'fa fa-chevron-right'
17598 initEvents: function()
17600 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17601 // this.el.on("touchstart", this.onTouchStart, this);
17604 if(this.autoslide){
17607 this.slideFn = window.setInterval(function() {
17608 _this.showPanelNext();
17612 if(this.showarrow){
17613 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17614 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17620 // onTouchStart : function(e, el, o)
17622 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17626 // this.showPanelNext();
17630 getChildContainer : function()
17632 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17636 * register a Navigation item
17637 * @param {Roo.bootstrap.NavItem} the navitem to add
17639 register : function(item)
17641 this.tabs.push( item);
17642 item.navId = this.navId; // not really needed..
17647 getActivePanel : function()
17650 Roo.each(this.tabs, function(t) {
17660 getPanelByName : function(n)
17663 Roo.each(this.tabs, function(t) {
17664 if (t.tabId == n) {
17672 indexOfPanel : function(p)
17675 Roo.each(this.tabs, function(t,i) {
17676 if (t.tabId == p.tabId) {
17685 * show a specific panel
17686 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17687 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17689 showPanel : function (pan)
17691 if(this.transition || typeof(pan) == 'undefined'){
17692 Roo.log("waiting for the transitionend");
17696 if (typeof(pan) == 'number') {
17697 pan = this.tabs[pan];
17700 if (typeof(pan) == 'string') {
17701 pan = this.getPanelByName(pan);
17704 var cur = this.getActivePanel();
17707 Roo.log('pan or acitve pan is undefined');
17711 if (pan.tabId == this.getActivePanel().tabId) {
17715 if (false === cur.fireEvent('beforedeactivate')) {
17719 if(this.bullets > 0 && !Roo.isTouch){
17720 this.setActiveBullet(this.indexOfPanel(pan));
17723 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17725 this.transition = true;
17726 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17727 var lr = dir == 'next' ? 'left' : 'right';
17728 pan.el.addClass(dir); // or prev
17729 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17730 cur.el.addClass(lr); // or right
17731 pan.el.addClass(lr);
17734 cur.el.on('transitionend', function() {
17735 Roo.log("trans end?");
17737 pan.el.removeClass([lr,dir]);
17738 pan.setActive(true);
17740 cur.el.removeClass([lr]);
17741 cur.setActive(false);
17743 _this.transition = false;
17745 }, this, { single: true } );
17750 cur.setActive(false);
17751 pan.setActive(true);
17756 showPanelNext : function()
17758 var i = this.indexOfPanel(this.getActivePanel());
17760 if (i >= this.tabs.length - 1 && !this.autoslide) {
17764 if (i >= this.tabs.length - 1 && this.autoslide) {
17768 this.showPanel(this.tabs[i+1]);
17771 showPanelPrev : function()
17773 var i = this.indexOfPanel(this.getActivePanel());
17775 if (i < 1 && !this.autoslide) {
17779 if (i < 1 && this.autoslide) {
17780 i = this.tabs.length;
17783 this.showPanel(this.tabs[i-1]);
17787 addBullet: function()
17789 if(!this.bullets || Roo.isTouch){
17792 var ctr = this.el.select('.carousel-bullets',true).first();
17793 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17794 var bullet = ctr.createChild({
17795 cls : 'bullet bullet-' + i
17796 },ctr.dom.lastChild);
17801 bullet.on('click', (function(e, el, o, ii, t){
17803 e.preventDefault();
17805 this.showPanel(ii);
17807 if(this.autoslide && this.slideFn){
17808 clearInterval(this.slideFn);
17809 this.slideFn = window.setInterval(function() {
17810 _this.showPanelNext();
17814 }).createDelegate(this, [i, bullet], true));
17819 setActiveBullet : function(i)
17825 Roo.each(this.el.select('.bullet', true).elements, function(el){
17826 el.removeClass('selected');
17829 var bullet = this.el.select('.bullet-' + i, true).first();
17835 bullet.addClass('selected');
17846 Roo.apply(Roo.bootstrap.TabGroup, {
17850 * register a Navigation Group
17851 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17853 register : function(navgrp)
17855 this.groups[navgrp.navId] = navgrp;
17859 * fetch a Navigation Group based on the navigation ID
17860 * if one does not exist , it will get created.
17861 * @param {string} the navgroup to add
17862 * @returns {Roo.bootstrap.NavGroup} the navgroup
17864 get: function(navId) {
17865 if (typeof(this.groups[navId]) == 'undefined') {
17866 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17868 return this.groups[navId] ;
17883 * @class Roo.bootstrap.TabPanel
17884 * @extends Roo.bootstrap.Component
17885 * Bootstrap TabPanel class
17886 * @cfg {Boolean} active panel active
17887 * @cfg {String} html panel content
17888 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17889 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17890 * @cfg {String} href click to link..
17894 * Create a new TabPanel
17895 * @param {Object} config The config object
17898 Roo.bootstrap.TabPanel = function(config){
17899 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17903 * Fires when the active status changes
17904 * @param {Roo.bootstrap.TabPanel} this
17905 * @param {Boolean} state the new state
17910 * @event beforedeactivate
17911 * Fires before a tab is de-activated - can be used to do validation on a form.
17912 * @param {Roo.bootstrap.TabPanel} this
17913 * @return {Boolean} false if there is an error
17916 'beforedeactivate': true
17919 this.tabId = this.tabId || Roo.id();
17923 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17931 getAutoCreate : function(){
17934 // item is needed for carousel - not sure if it has any effect otherwise
17935 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17936 html: this.html || ''
17940 cfg.cls += ' active';
17944 cfg.tabId = this.tabId;
17951 initEvents: function()
17953 var p = this.parent();
17955 this.navId = this.navId || p.navId;
17957 if (typeof(this.navId) != 'undefined') {
17958 // not really needed.. but just in case.. parent should be a NavGroup.
17959 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17963 var i = tg.tabs.length - 1;
17965 if(this.active && tg.bullets > 0 && i < tg.bullets){
17966 tg.setActiveBullet(i);
17970 this.el.on('click', this.onClick, this);
17973 this.el.on("touchstart", this.onTouchStart, this);
17974 this.el.on("touchmove", this.onTouchMove, this);
17975 this.el.on("touchend", this.onTouchEnd, this);
17980 onRender : function(ct, position)
17982 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17985 setActive : function(state)
17987 Roo.log("panel - set active " + this.tabId + "=" + state);
17989 this.active = state;
17991 this.el.removeClass('active');
17993 } else if (!this.el.hasClass('active')) {
17994 this.el.addClass('active');
17997 this.fireEvent('changed', this, state);
18000 onClick : function(e)
18002 e.preventDefault();
18004 if(!this.href.length){
18008 window.location.href = this.href;
18017 onTouchStart : function(e)
18019 this.swiping = false;
18021 this.startX = e.browserEvent.touches[0].clientX;
18022 this.startY = e.browserEvent.touches[0].clientY;
18025 onTouchMove : function(e)
18027 this.swiping = true;
18029 this.endX = e.browserEvent.touches[0].clientX;
18030 this.endY = e.browserEvent.touches[0].clientY;
18033 onTouchEnd : function(e)
18040 var tabGroup = this.parent();
18042 if(this.endX > this.startX){ // swiping right
18043 tabGroup.showPanelPrev();
18047 if(this.startX > this.endX){ // swiping left
18048 tabGroup.showPanelNext();
18067 * @class Roo.bootstrap.DateField
18068 * @extends Roo.bootstrap.Input
18069 * Bootstrap DateField class
18070 * @cfg {Number} weekStart default 0
18071 * @cfg {String} viewMode default empty, (months|years)
18072 * @cfg {String} minViewMode default empty, (months|years)
18073 * @cfg {Number} startDate default -Infinity
18074 * @cfg {Number} endDate default Infinity
18075 * @cfg {Boolean} todayHighlight default false
18076 * @cfg {Boolean} todayBtn default false
18077 * @cfg {Boolean} calendarWeeks default false
18078 * @cfg {Object} daysOfWeekDisabled default empty
18079 * @cfg {Boolean} singleMode default false (true | false)
18081 * @cfg {Boolean} keyboardNavigation default true
18082 * @cfg {String} language default en
18085 * Create a new DateField
18086 * @param {Object} config The config object
18089 Roo.bootstrap.DateField = function(config){
18090 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18094 * Fires when this field show.
18095 * @param {Roo.bootstrap.DateField} this
18096 * @param {Mixed} date The date value
18101 * Fires when this field hide.
18102 * @param {Roo.bootstrap.DateField} this
18103 * @param {Mixed} date The date value
18108 * Fires when select a date.
18109 * @param {Roo.bootstrap.DateField} this
18110 * @param {Mixed} date The date value
18114 * @event beforeselect
18115 * Fires when before select a date.
18116 * @param {Roo.bootstrap.DateField} this
18117 * @param {Mixed} date The date value
18119 beforeselect : true
18123 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18126 * @cfg {String} format
18127 * The default date format string which can be overriden for localization support. The format must be
18128 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18132 * @cfg {String} altFormats
18133 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18134 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18136 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18144 todayHighlight : false,
18150 keyboardNavigation: true,
18152 calendarWeeks: false,
18154 startDate: -Infinity,
18158 daysOfWeekDisabled: [],
18162 singleMode : false,
18164 UTCDate: function()
18166 return new Date(Date.UTC.apply(Date, arguments));
18169 UTCToday: function()
18171 var today = new Date();
18172 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18175 getDate: function() {
18176 var d = this.getUTCDate();
18177 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18180 getUTCDate: function() {
18184 setDate: function(d) {
18185 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18188 setUTCDate: function(d) {
18190 this.setValue(this.formatDate(this.date));
18193 onRender: function(ct, position)
18196 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18198 this.language = this.language || 'en';
18199 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18200 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18202 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18203 this.format = this.format || 'm/d/y';
18204 this.isInline = false;
18205 this.isInput = true;
18206 this.component = this.el.select('.add-on', true).first() || false;
18207 this.component = (this.component && this.component.length === 0) ? false : this.component;
18208 this.hasInput = this.component && this.inputEl().length;
18210 if (typeof(this.minViewMode === 'string')) {
18211 switch (this.minViewMode) {
18213 this.minViewMode = 1;
18216 this.minViewMode = 2;
18219 this.minViewMode = 0;
18224 if (typeof(this.viewMode === 'string')) {
18225 switch (this.viewMode) {
18238 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18240 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18242 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18244 this.picker().on('mousedown', this.onMousedown, this);
18245 this.picker().on('click', this.onClick, this);
18247 this.picker().addClass('datepicker-dropdown');
18249 this.startViewMode = this.viewMode;
18251 if(this.singleMode){
18252 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18253 v.setVisibilityMode(Roo.Element.DISPLAY);
18257 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18258 v.setStyle('width', '189px');
18262 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18263 if(!this.calendarWeeks){
18268 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18269 v.attr('colspan', function(i, val){
18270 return parseInt(val) + 1;
18275 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18277 this.setStartDate(this.startDate);
18278 this.setEndDate(this.endDate);
18280 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18287 if(this.isInline) {
18292 picker : function()
18294 return this.pickerEl;
18295 // return this.el.select('.datepicker', true).first();
18298 fillDow: function()
18300 var dowCnt = this.weekStart;
18309 if(this.calendarWeeks){
18317 while (dowCnt < this.weekStart + 7) {
18321 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18325 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18328 fillMonths: function()
18331 var months = this.picker().select('>.datepicker-months td', true).first();
18333 months.dom.innerHTML = '';
18339 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18342 months.createChild(month);
18349 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;
18351 if (this.date < this.startDate) {
18352 this.viewDate = new Date(this.startDate);
18353 } else if (this.date > this.endDate) {
18354 this.viewDate = new Date(this.endDate);
18356 this.viewDate = new Date(this.date);
18364 var d = new Date(this.viewDate),
18365 year = d.getUTCFullYear(),
18366 month = d.getUTCMonth(),
18367 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18368 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18369 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18370 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18371 currentDate = this.date && this.date.valueOf(),
18372 today = this.UTCToday();
18374 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18376 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18378 // this.picker.select('>tfoot th.today').
18379 // .text(dates[this.language].today)
18380 // .toggle(this.todayBtn !== false);
18382 this.updateNavArrows();
18385 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18387 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18389 prevMonth.setUTCDate(day);
18391 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18393 var nextMonth = new Date(prevMonth);
18395 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18397 nextMonth = nextMonth.valueOf();
18399 var fillMonths = false;
18401 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18403 while(prevMonth.valueOf() < nextMonth) {
18406 if (prevMonth.getUTCDay() === this.weekStart) {
18408 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18416 if(this.calendarWeeks){
18417 // ISO 8601: First week contains first thursday.
18418 // ISO also states week starts on Monday, but we can be more abstract here.
18420 // Start of current week: based on weekstart/current date
18421 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18422 // Thursday of this week
18423 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18424 // First Thursday of year, year from thursday
18425 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18426 // Calendar week: ms between thursdays, div ms per day, div 7 days
18427 calWeek = (th - yth) / 864e5 / 7 + 1;
18429 fillMonths.cn.push({
18437 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18439 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18442 if (this.todayHighlight &&
18443 prevMonth.getUTCFullYear() == today.getFullYear() &&
18444 prevMonth.getUTCMonth() == today.getMonth() &&
18445 prevMonth.getUTCDate() == today.getDate()) {
18446 clsName += ' today';
18449 if (currentDate && prevMonth.valueOf() === currentDate) {
18450 clsName += ' active';
18453 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18454 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18455 clsName += ' disabled';
18458 fillMonths.cn.push({
18460 cls: 'day ' + clsName,
18461 html: prevMonth.getDate()
18464 prevMonth.setDate(prevMonth.getDate()+1);
18467 var currentYear = this.date && this.date.getUTCFullYear();
18468 var currentMonth = this.date && this.date.getUTCMonth();
18470 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18472 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18473 v.removeClass('active');
18475 if(currentYear === year && k === currentMonth){
18476 v.addClass('active');
18479 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18480 v.addClass('disabled');
18486 year = parseInt(year/10, 10) * 10;
18488 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18490 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18493 for (var i = -1; i < 11; i++) {
18494 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18496 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18504 showMode: function(dir)
18507 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18510 Roo.each(this.picker().select('>div',true).elements, function(v){
18511 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18514 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18519 if(this.isInline) {
18523 this.picker().removeClass(['bottom', 'top']);
18525 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18527 * place to the top of element!
18531 this.picker().addClass('top');
18532 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18537 this.picker().addClass('bottom');
18539 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18542 parseDate : function(value)
18544 if(!value || value instanceof Date){
18547 var v = Date.parseDate(value, this.format);
18548 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18549 v = Date.parseDate(value, 'Y-m-d');
18551 if(!v && this.altFormats){
18552 if(!this.altFormatsArray){
18553 this.altFormatsArray = this.altFormats.split("|");
18555 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18556 v = Date.parseDate(value, this.altFormatsArray[i]);
18562 formatDate : function(date, fmt)
18564 return (!date || !(date instanceof Date)) ?
18565 date : date.dateFormat(fmt || this.format);
18568 onFocus : function()
18570 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18574 onBlur : function()
18576 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18578 var d = this.inputEl().getValue();
18587 this.picker().show();
18591 this.fireEvent('show', this, this.date);
18596 if(this.isInline) {
18599 this.picker().hide();
18600 this.viewMode = this.startViewMode;
18603 this.fireEvent('hide', this, this.date);
18607 onMousedown: function(e)
18609 e.stopPropagation();
18610 e.preventDefault();
18615 Roo.bootstrap.DateField.superclass.keyup.call(this);
18619 setValue: function(v)
18621 if(this.fireEvent('beforeselect', this, v) !== false){
18622 var d = new Date(this.parseDate(v) ).clearTime();
18624 if(isNaN(d.getTime())){
18625 this.date = this.viewDate = '';
18626 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18630 v = this.formatDate(d);
18632 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18634 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18638 this.fireEvent('select', this, this.date);
18642 getValue: function()
18644 return this.formatDate(this.date);
18647 fireKey: function(e)
18649 if (!this.picker().isVisible()){
18650 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18656 var dateChanged = false,
18658 newDate, newViewDate;
18663 e.preventDefault();
18667 if (!this.keyboardNavigation) {
18670 dir = e.keyCode == 37 ? -1 : 1;
18673 newDate = this.moveYear(this.date, dir);
18674 newViewDate = this.moveYear(this.viewDate, dir);
18675 } else if (e.shiftKey){
18676 newDate = this.moveMonth(this.date, dir);
18677 newViewDate = this.moveMonth(this.viewDate, dir);
18679 newDate = new Date(this.date);
18680 newDate.setUTCDate(this.date.getUTCDate() + dir);
18681 newViewDate = new Date(this.viewDate);
18682 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18684 if (this.dateWithinRange(newDate)){
18685 this.date = newDate;
18686 this.viewDate = newViewDate;
18687 this.setValue(this.formatDate(this.date));
18689 e.preventDefault();
18690 dateChanged = true;
18695 if (!this.keyboardNavigation) {
18698 dir = e.keyCode == 38 ? -1 : 1;
18700 newDate = this.moveYear(this.date, dir);
18701 newViewDate = this.moveYear(this.viewDate, dir);
18702 } else if (e.shiftKey){
18703 newDate = this.moveMonth(this.date, dir);
18704 newViewDate = this.moveMonth(this.viewDate, dir);
18706 newDate = new Date(this.date);
18707 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18708 newViewDate = new Date(this.viewDate);
18709 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18711 if (this.dateWithinRange(newDate)){
18712 this.date = newDate;
18713 this.viewDate = newViewDate;
18714 this.setValue(this.formatDate(this.date));
18716 e.preventDefault();
18717 dateChanged = true;
18721 this.setValue(this.formatDate(this.date));
18723 e.preventDefault();
18726 this.setValue(this.formatDate(this.date));
18740 onClick: function(e)
18742 e.stopPropagation();
18743 e.preventDefault();
18745 var target = e.getTarget();
18747 if(target.nodeName.toLowerCase() === 'i'){
18748 target = Roo.get(target).dom.parentNode;
18751 var nodeName = target.nodeName;
18752 var className = target.className;
18753 var html = target.innerHTML;
18754 //Roo.log(nodeName);
18756 switch(nodeName.toLowerCase()) {
18758 switch(className) {
18764 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18765 switch(this.viewMode){
18767 this.viewDate = this.moveMonth(this.viewDate, dir);
18771 this.viewDate = this.moveYear(this.viewDate, dir);
18777 var date = new Date();
18778 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18780 this.setValue(this.formatDate(this.date));
18787 if (className.indexOf('disabled') < 0) {
18788 this.viewDate.setUTCDate(1);
18789 if (className.indexOf('month') > -1) {
18790 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18792 var year = parseInt(html, 10) || 0;
18793 this.viewDate.setUTCFullYear(year);
18797 if(this.singleMode){
18798 this.setValue(this.formatDate(this.viewDate));
18809 //Roo.log(className);
18810 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18811 var day = parseInt(html, 10) || 1;
18812 var year = this.viewDate.getUTCFullYear(),
18813 month = this.viewDate.getUTCMonth();
18815 if (className.indexOf('old') > -1) {
18822 } else if (className.indexOf('new') > -1) {
18830 //Roo.log([year,month,day]);
18831 this.date = this.UTCDate(year, month, day,0,0,0,0);
18832 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18834 //Roo.log(this.formatDate(this.date));
18835 this.setValue(this.formatDate(this.date));
18842 setStartDate: function(startDate)
18844 this.startDate = startDate || -Infinity;
18845 if (this.startDate !== -Infinity) {
18846 this.startDate = this.parseDate(this.startDate);
18849 this.updateNavArrows();
18852 setEndDate: function(endDate)
18854 this.endDate = endDate || Infinity;
18855 if (this.endDate !== Infinity) {
18856 this.endDate = this.parseDate(this.endDate);
18859 this.updateNavArrows();
18862 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18864 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18865 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18866 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18868 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18869 return parseInt(d, 10);
18872 this.updateNavArrows();
18875 updateNavArrows: function()
18877 if(this.singleMode){
18881 var d = new Date(this.viewDate),
18882 year = d.getUTCFullYear(),
18883 month = d.getUTCMonth();
18885 Roo.each(this.picker().select('.prev', true).elements, function(v){
18887 switch (this.viewMode) {
18890 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18896 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18903 Roo.each(this.picker().select('.next', true).elements, function(v){
18905 switch (this.viewMode) {
18908 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18914 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18922 moveMonth: function(date, dir)
18927 var new_date = new Date(date.valueOf()),
18928 day = new_date.getUTCDate(),
18929 month = new_date.getUTCMonth(),
18930 mag = Math.abs(dir),
18932 dir = dir > 0 ? 1 : -1;
18935 // If going back one month, make sure month is not current month
18936 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18938 return new_date.getUTCMonth() == month;
18940 // If going forward one month, make sure month is as expected
18941 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18943 return new_date.getUTCMonth() != new_month;
18945 new_month = month + dir;
18946 new_date.setUTCMonth(new_month);
18947 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18948 if (new_month < 0 || new_month > 11) {
18949 new_month = (new_month + 12) % 12;
18952 // For magnitudes >1, move one month at a time...
18953 for (var i=0; i<mag; i++) {
18954 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18955 new_date = this.moveMonth(new_date, dir);
18957 // ...then reset the day, keeping it in the new month
18958 new_month = new_date.getUTCMonth();
18959 new_date.setUTCDate(day);
18961 return new_month != new_date.getUTCMonth();
18964 // Common date-resetting loop -- if date is beyond end of month, make it
18967 new_date.setUTCDate(--day);
18968 new_date.setUTCMonth(new_month);
18973 moveYear: function(date, dir)
18975 return this.moveMonth(date, dir*12);
18978 dateWithinRange: function(date)
18980 return date >= this.startDate && date <= this.endDate;
18986 this.picker().remove();
18989 validateValue : function(value)
18991 if(value.length < 1) {
18992 if(this.allowBlank){
18998 if(value.length < this.minLength){
19001 if(value.length > this.maxLength){
19005 var vt = Roo.form.VTypes;
19006 if(!vt[this.vtype](value, this)){
19010 if(typeof this.validator == "function"){
19011 var msg = this.validator(value);
19017 if(this.regex && !this.regex.test(value)){
19021 if(typeof(this.parseDate(value)) == 'undefined'){
19025 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19029 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19039 Roo.apply(Roo.bootstrap.DateField, {
19050 html: '<i class="fa fa-arrow-left"/>'
19060 html: '<i class="fa fa-arrow-right"/>'
19102 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19103 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19104 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19105 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19106 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19119 navFnc: 'FullYear',
19124 navFnc: 'FullYear',
19129 Roo.apply(Roo.bootstrap.DateField, {
19133 cls: 'datepicker dropdown-menu roo-dynamic',
19137 cls: 'datepicker-days',
19141 cls: 'table-condensed',
19143 Roo.bootstrap.DateField.head,
19147 Roo.bootstrap.DateField.footer
19154 cls: 'datepicker-months',
19158 cls: 'table-condensed',
19160 Roo.bootstrap.DateField.head,
19161 Roo.bootstrap.DateField.content,
19162 Roo.bootstrap.DateField.footer
19169 cls: 'datepicker-years',
19173 cls: 'table-condensed',
19175 Roo.bootstrap.DateField.head,
19176 Roo.bootstrap.DateField.content,
19177 Roo.bootstrap.DateField.footer
19196 * @class Roo.bootstrap.TimeField
19197 * @extends Roo.bootstrap.Input
19198 * Bootstrap DateField class
19202 * Create a new TimeField
19203 * @param {Object} config The config object
19206 Roo.bootstrap.TimeField = function(config){
19207 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19211 * Fires when this field show.
19212 * @param {Roo.bootstrap.DateField} thisthis
19213 * @param {Mixed} date The date value
19218 * Fires when this field hide.
19219 * @param {Roo.bootstrap.DateField} this
19220 * @param {Mixed} date The date value
19225 * Fires when select a date.
19226 * @param {Roo.bootstrap.DateField} this
19227 * @param {Mixed} date The date value
19233 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19236 * @cfg {String} format
19237 * The default time format string which can be overriden for localization support. The format must be
19238 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19242 onRender: function(ct, position)
19245 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19247 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19249 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19251 this.pop = this.picker().select('>.datepicker-time',true).first();
19252 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19254 this.picker().on('mousedown', this.onMousedown, this);
19255 this.picker().on('click', this.onClick, this);
19257 this.picker().addClass('datepicker-dropdown');
19262 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19263 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19264 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19265 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19266 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19267 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19271 fireKey: function(e){
19272 if (!this.picker().isVisible()){
19273 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19279 e.preventDefault();
19287 this.onTogglePeriod();
19290 this.onIncrementMinutes();
19293 this.onDecrementMinutes();
19302 onClick: function(e) {
19303 e.stopPropagation();
19304 e.preventDefault();
19307 picker : function()
19309 return this.el.select('.datepicker', true).first();
19312 fillTime: function()
19314 var time = this.pop.select('tbody', true).first();
19316 time.dom.innerHTML = '';
19331 cls: 'hours-up glyphicon glyphicon-chevron-up'
19351 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19372 cls: 'timepicker-hour',
19387 cls: 'timepicker-minute',
19402 cls: 'btn btn-primary period',
19424 cls: 'hours-down glyphicon glyphicon-chevron-down'
19444 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19462 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19469 var hours = this.time.getHours();
19470 var minutes = this.time.getMinutes();
19483 hours = hours - 12;
19487 hours = '0' + hours;
19491 minutes = '0' + minutes;
19494 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19495 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19496 this.pop.select('button', true).first().dom.innerHTML = period;
19502 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19504 var cls = ['bottom'];
19506 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19513 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19518 this.picker().addClass(cls.join('-'));
19522 Roo.each(cls, function(c){
19524 _this.picker().setTop(_this.inputEl().getHeight());
19528 _this.picker().setTop(0 - _this.picker().getHeight());
19533 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19537 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19544 onFocus : function()
19546 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19550 onBlur : function()
19552 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19558 this.picker().show();
19563 this.fireEvent('show', this, this.date);
19568 this.picker().hide();
19571 this.fireEvent('hide', this, this.date);
19574 setTime : function()
19577 this.setValue(this.time.format(this.format));
19579 this.fireEvent('select', this, this.date);
19584 onMousedown: function(e){
19585 e.stopPropagation();
19586 e.preventDefault();
19589 onIncrementHours: function()
19591 Roo.log('onIncrementHours');
19592 this.time = this.time.add(Date.HOUR, 1);
19597 onDecrementHours: function()
19599 Roo.log('onDecrementHours');
19600 this.time = this.time.add(Date.HOUR, -1);
19604 onIncrementMinutes: function()
19606 Roo.log('onIncrementMinutes');
19607 this.time = this.time.add(Date.MINUTE, 1);
19611 onDecrementMinutes: function()
19613 Roo.log('onDecrementMinutes');
19614 this.time = this.time.add(Date.MINUTE, -1);
19618 onTogglePeriod: function()
19620 Roo.log('onTogglePeriod');
19621 this.time = this.time.add(Date.HOUR, 12);
19628 Roo.apply(Roo.bootstrap.TimeField, {
19658 cls: 'btn btn-info ok',
19670 Roo.apply(Roo.bootstrap.TimeField, {
19674 cls: 'datepicker dropdown-menu',
19678 cls: 'datepicker-time',
19682 cls: 'table-condensed',
19684 Roo.bootstrap.TimeField.content,
19685 Roo.bootstrap.TimeField.footer
19704 * @class Roo.bootstrap.MonthField
19705 * @extends Roo.bootstrap.Input
19706 * Bootstrap MonthField class
19708 * @cfg {String} language default en
19711 * Create a new MonthField
19712 * @param {Object} config The config object
19715 Roo.bootstrap.MonthField = function(config){
19716 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19721 * Fires when this field show.
19722 * @param {Roo.bootstrap.MonthField} this
19723 * @param {Mixed} date The date value
19728 * Fires when this field hide.
19729 * @param {Roo.bootstrap.MonthField} this
19730 * @param {Mixed} date The date value
19735 * Fires when select a date.
19736 * @param {Roo.bootstrap.MonthField} this
19737 * @param {String} oldvalue The old value
19738 * @param {String} newvalue The new value
19744 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19746 onRender: function(ct, position)
19749 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19751 this.language = this.language || 'en';
19752 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19753 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19755 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19756 this.isInline = false;
19757 this.isInput = true;
19758 this.component = this.el.select('.add-on', true).first() || false;
19759 this.component = (this.component && this.component.length === 0) ? false : this.component;
19760 this.hasInput = this.component && this.inputEL().length;
19762 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19764 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19766 this.picker().on('mousedown', this.onMousedown, this);
19767 this.picker().on('click', this.onClick, this);
19769 this.picker().addClass('datepicker-dropdown');
19771 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19772 v.setStyle('width', '189px');
19779 if(this.isInline) {
19785 setValue: function(v, suppressEvent)
19787 var o = this.getValue();
19789 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19793 if(suppressEvent !== true){
19794 this.fireEvent('select', this, o, v);
19799 getValue: function()
19804 onClick: function(e)
19806 e.stopPropagation();
19807 e.preventDefault();
19809 var target = e.getTarget();
19811 if(target.nodeName.toLowerCase() === 'i'){
19812 target = Roo.get(target).dom.parentNode;
19815 var nodeName = target.nodeName;
19816 var className = target.className;
19817 var html = target.innerHTML;
19819 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19823 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19825 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19831 picker : function()
19833 return this.pickerEl;
19836 fillMonths: function()
19839 var months = this.picker().select('>.datepicker-months td', true).first();
19841 months.dom.innerHTML = '';
19847 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19850 months.createChild(month);
19859 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19860 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19863 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19864 e.removeClass('active');
19866 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19867 e.addClass('active');
19874 if(this.isInline) {
19878 this.picker().removeClass(['bottom', 'top']);
19880 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19882 * place to the top of element!
19886 this.picker().addClass('top');
19887 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19892 this.picker().addClass('bottom');
19894 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19897 onFocus : function()
19899 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19903 onBlur : function()
19905 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19907 var d = this.inputEl().getValue();
19916 this.picker().show();
19917 this.picker().select('>.datepicker-months', true).first().show();
19921 this.fireEvent('show', this, this.date);
19926 if(this.isInline) {
19929 this.picker().hide();
19930 this.fireEvent('hide', this, this.date);
19934 onMousedown: function(e)
19936 e.stopPropagation();
19937 e.preventDefault();
19942 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19946 fireKey: function(e)
19948 if (!this.picker().isVisible()){
19949 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19960 e.preventDefault();
19964 dir = e.keyCode == 37 ? -1 : 1;
19966 this.vIndex = this.vIndex + dir;
19968 if(this.vIndex < 0){
19972 if(this.vIndex > 11){
19976 if(isNaN(this.vIndex)){
19980 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19986 dir = e.keyCode == 38 ? -1 : 1;
19988 this.vIndex = this.vIndex + dir * 4;
19990 if(this.vIndex < 0){
19994 if(this.vIndex > 11){
19998 if(isNaN(this.vIndex)){
20002 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20007 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20008 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20012 e.preventDefault();
20015 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20016 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20032 this.picker().remove();
20037 Roo.apply(Roo.bootstrap.MonthField, {
20056 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20057 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20062 Roo.apply(Roo.bootstrap.MonthField, {
20066 cls: 'datepicker dropdown-menu roo-dynamic',
20070 cls: 'datepicker-months',
20074 cls: 'table-condensed',
20076 Roo.bootstrap.DateField.content
20096 * @class Roo.bootstrap.CheckBox
20097 * @extends Roo.bootstrap.Input
20098 * Bootstrap CheckBox class
20100 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20101 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20102 * @cfg {String} boxLabel The text that appears beside the checkbox
20103 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20104 * @cfg {Boolean} checked initnal the element
20105 * @cfg {Boolean} inline inline the element (default false)
20106 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20109 * Create a new CheckBox
20110 * @param {Object} config The config object
20113 Roo.bootstrap.CheckBox = function(config){
20114 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20119 * Fires when the element is checked or unchecked.
20120 * @param {Roo.bootstrap.CheckBox} this This input
20121 * @param {Boolean} checked The new checked value
20128 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20130 inputType: 'checkbox',
20138 getAutoCreate : function()
20140 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20146 cfg.cls = 'form-group ' + this.inputType; //input-group
20149 cfg.cls += ' ' + this.inputType + '-inline';
20155 type : this.inputType,
20156 value : this.inputValue,
20157 cls : 'roo-' + this.inputType, //'form-box',
20158 placeholder : this.placeholder || ''
20162 if(this.inputType != 'radio'){
20166 cls : 'roo-hidden-value',
20167 value : this.checked ? this.valueOff : this.inputValue
20172 if (this.weight) { // Validity check?
20173 cfg.cls += " " + this.inputType + "-" + this.weight;
20176 if (this.disabled) {
20177 input.disabled=true;
20181 input.checked = this.checked;
20188 input.name = this.name;
20190 if(this.inputType != 'radio'){
20191 hidden.name = this.name;
20192 input.name = '_hidden_' + this.name;
20197 input.cls += ' input-' + this.size;
20202 ['xs','sm','md','lg'].map(function(size){
20203 if (settings[size]) {
20204 cfg.cls += ' col-' + size + '-' + settings[size];
20208 var inputblock = input;
20210 if (this.before || this.after) {
20213 cls : 'input-group',
20218 inputblock.cn.push({
20220 cls : 'input-group-addon',
20225 inputblock.cn.push(input);
20227 if(this.inputType != 'radio'){
20228 inputblock.cn.push(hidden);
20232 inputblock.cn.push({
20234 cls : 'input-group-addon',
20241 if (align ==='left' && this.fieldLabel.length) {
20242 // Roo.log("left and has label");
20247 cls : 'control-label',
20248 html : this.fieldLabel
20259 if(this.labelWidth > 12){
20260 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20263 if(this.labelWidth < 13 && this.labelmd == 0){
20264 this.labelmd = this.labelWidth;
20267 if(this.labellg > 0){
20268 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20269 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20272 if(this.labelmd > 0){
20273 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20274 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20277 if(this.labelsm > 0){
20278 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20279 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20282 if(this.labelxs > 0){
20283 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20284 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20287 } else if ( this.fieldLabel.length) {
20288 // Roo.log(" label");
20292 tag: this.boxLabel ? 'span' : 'label',
20294 cls: 'control-label box-input-label',
20295 //cls : 'input-group-addon',
20296 html : this.fieldLabel
20306 // Roo.log(" no label && no align");
20307 cfg.cn = [ inputblock ] ;
20313 var boxLabelCfg = {
20315 //'for': id, // box label is handled by onclick - so no for...
20317 html: this.boxLabel
20321 boxLabelCfg.tooltip = this.tooltip;
20324 cfg.cn.push(boxLabelCfg);
20327 if(this.inputType != 'radio'){
20328 cfg.cn.push(hidden);
20336 * return the real input element.
20338 inputEl: function ()
20340 return this.el.select('input.roo-' + this.inputType,true).first();
20342 hiddenEl: function ()
20344 return this.el.select('input.roo-hidden-value',true).first();
20347 labelEl: function()
20349 return this.el.select('label.control-label',true).first();
20351 /* depricated... */
20355 return this.labelEl();
20358 boxLabelEl: function()
20360 return this.el.select('label.box-label',true).first();
20363 initEvents : function()
20365 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20367 this.inputEl().on('click', this.onClick, this);
20369 if (this.boxLabel) {
20370 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20373 this.startValue = this.getValue();
20376 Roo.bootstrap.CheckBox.register(this);
20380 onClick : function()
20382 this.setChecked(!this.checked);
20385 setChecked : function(state,suppressEvent)
20387 this.startValue = this.getValue();
20389 if(this.inputType == 'radio'){
20391 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20392 e.dom.checked = false;
20395 this.inputEl().dom.checked = true;
20397 this.inputEl().dom.value = this.inputValue;
20399 if(suppressEvent !== true){
20400 this.fireEvent('check', this, true);
20408 this.checked = state;
20410 this.inputEl().dom.checked = state;
20413 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20415 if(suppressEvent !== true){
20416 this.fireEvent('check', this, state);
20422 getValue : function()
20424 if(this.inputType == 'radio'){
20425 return this.getGroupValue();
20428 return this.hiddenEl().dom.value;
20432 getGroupValue : function()
20434 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20438 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20441 setValue : function(v,suppressEvent)
20443 if(this.inputType == 'radio'){
20444 this.setGroupValue(v, suppressEvent);
20448 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20453 setGroupValue : function(v, suppressEvent)
20455 this.startValue = this.getValue();
20457 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20458 e.dom.checked = false;
20460 if(e.dom.value == v){
20461 e.dom.checked = true;
20465 if(suppressEvent !== true){
20466 this.fireEvent('check', this, true);
20474 validate : function()
20478 (this.inputType == 'radio' && this.validateRadio()) ||
20479 (this.inputType == 'checkbox' && this.validateCheckbox())
20485 this.markInvalid();
20489 validateRadio : function()
20491 if(this.allowBlank){
20497 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20498 if(!e.dom.checked){
20510 validateCheckbox : function()
20513 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20514 //return (this.getValue() == this.inputValue) ? true : false;
20517 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20525 for(var i in group){
20530 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20537 * Mark this field as valid
20539 markValid : function()
20543 this.fireEvent('valid', this);
20545 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20548 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20555 if(this.inputType == 'radio'){
20556 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20557 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20558 e.findParent('.form-group', false, true).addClass(_this.validClass);
20565 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20566 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20570 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20576 for(var i in group){
20577 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20578 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20583 * Mark this field as invalid
20584 * @param {String} msg The validation message
20586 markInvalid : function(msg)
20588 if(this.allowBlank){
20594 this.fireEvent('invalid', this, msg);
20596 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20599 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20603 label.markInvalid();
20606 if(this.inputType == 'radio'){
20607 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20608 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20609 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20616 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20617 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20621 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20627 for(var i in group){
20628 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20629 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20634 clearInvalid : function()
20636 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20638 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20640 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20643 label.iconEl.removeClass(label.validClass);
20644 label.iconEl.removeClass(label.invalidClass);
20648 disable : function()
20650 if(this.inputType != 'radio'){
20651 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20658 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20659 _this.getActionEl().addClass(this.disabledClass);
20660 e.dom.disabled = true;
20664 this.disabled = true;
20665 this.fireEvent("disable", this);
20669 enable : function()
20671 if(this.inputType != 'radio'){
20672 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20679 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20680 _this.getActionEl().removeClass(this.disabledClass);
20681 e.dom.disabled = false;
20685 this.disabled = false;
20686 this.fireEvent("enable", this);
20692 Roo.apply(Roo.bootstrap.CheckBox, {
20697 * register a CheckBox Group
20698 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20700 register : function(checkbox)
20702 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20703 this.groups[checkbox.groupId] = {};
20706 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20710 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20714 * fetch a CheckBox Group based on the group ID
20715 * @param {string} the group ID
20716 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20718 get: function(groupId) {
20719 if (typeof(this.groups[groupId]) == 'undefined') {
20723 return this.groups[groupId] ;
20736 * @class Roo.bootstrap.Radio
20737 * @extends Roo.bootstrap.Component
20738 * Bootstrap Radio class
20739 * @cfg {String} boxLabel - the label associated
20740 * @cfg {String} value - the value of radio
20743 * Create a new Radio
20744 * @param {Object} config The config object
20746 Roo.bootstrap.Radio = function(config){
20747 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20751 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20757 getAutoCreate : function()
20761 cls : 'form-group radio',
20766 html : this.boxLabel
20774 initEvents : function()
20776 this.parent().register(this);
20778 this.el.on('click', this.onClick, this);
20782 onClick : function()
20784 this.setChecked(true);
20787 setChecked : function(state, suppressEvent)
20789 this.parent().setValue(this.value, suppressEvent);
20793 setBoxLabel : function(v)
20798 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20813 * @class Roo.bootstrap.SecurePass
20814 * @extends Roo.bootstrap.Input
20815 * Bootstrap SecurePass class
20819 * Create a new SecurePass
20820 * @param {Object} config The config object
20823 Roo.bootstrap.SecurePass = function (config) {
20824 // these go here, so the translation tool can replace them..
20826 PwdEmpty: "Please type a password, and then retype it to confirm.",
20827 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20828 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20829 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20830 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20831 FNInPwd: "Your password can't contain your first name. Please type a different password.",
20832 LNInPwd: "Your password can't contain your last name. Please type a different password.",
20833 TooWeak: "Your password is Too Weak."
20835 this.meterLabel = "Password strength:";
20836 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20837 this.meterClass = [
20838 "roo-password-meter-tooweak",
20839 "roo-password-meter-weak",
20840 "roo-password-meter-medium",
20841 "roo-password-meter-strong",
20842 "roo-password-meter-grey"
20847 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20850 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20852 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20854 * PwdEmpty: "Please type a password, and then retype it to confirm.",
20855 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20856 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20857 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20858 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20859 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
20860 * LNInPwd: "Your password can't contain your last name. Please type a different password."
20870 * @cfg {String/Object} Label for the strength meter (defaults to
20871 * 'Password strength:')
20876 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20877 * ['Weak', 'Medium', 'Strong'])
20880 pwdStrengths: false,
20893 initEvents: function ()
20895 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20897 if (this.el.is('input[type=password]') && Roo.isSafari) {
20898 this.el.on('keydown', this.SafariOnKeyDown, this);
20901 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20904 onRender: function (ct, position)
20906 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20907 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20908 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20910 this.trigger.createChild({
20915 cls: 'roo-password-meter-grey col-xs-12',
20918 //width: this.meterWidth + 'px'
20922 cls: 'roo-password-meter-text'
20928 if (this.hideTrigger) {
20929 this.trigger.setDisplayed(false);
20931 this.setSize(this.width || '', this.height || '');
20934 onDestroy: function ()
20936 if (this.trigger) {
20937 this.trigger.removeAllListeners();
20938 this.trigger.remove();
20941 this.wrap.remove();
20943 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20946 checkStrength: function ()
20948 var pwd = this.inputEl().getValue();
20949 if (pwd == this._lastPwd) {
20954 if (this.ClientSideStrongPassword(pwd)) {
20956 } else if (this.ClientSideMediumPassword(pwd)) {
20958 } else if (this.ClientSideWeakPassword(pwd)) {
20964 Roo.log('strength1: ' + strength);
20966 //var pm = this.trigger.child('div/div/div').dom;
20967 var pm = this.trigger.child('div/div');
20968 pm.removeClass(this.meterClass);
20969 pm.addClass(this.meterClass[strength]);
20972 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20974 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
20976 this._lastPwd = pwd;
20980 Roo.bootstrap.SecurePass.superclass.reset.call(this);
20982 this._lastPwd = '';
20984 var pm = this.trigger.child('div/div');
20985 pm.removeClass(this.meterClass);
20986 pm.addClass('roo-password-meter-grey');
20989 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20992 this.inputEl().dom.type='password';
20995 validateValue: function (value)
20998 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21001 if (value.length == 0) {
21002 if (this.allowBlank) {
21003 this.clearInvalid();
21007 this.markInvalid(this.errors.PwdEmpty);
21008 this.errorMsg = this.errors.PwdEmpty;
21016 if ('[\x21-\x7e]*'.match(value)) {
21017 this.markInvalid(this.errors.PwdBadChar);
21018 this.errorMsg = this.errors.PwdBadChar;
21021 if (value.length < 6) {
21022 this.markInvalid(this.errors.PwdShort);
21023 this.errorMsg = this.errors.PwdShort;
21026 if (value.length > 16) {
21027 this.markInvalid(this.errors.PwdLong);
21028 this.errorMsg = this.errors.PwdLong;
21032 if (this.ClientSideStrongPassword(value)) {
21034 } else if (this.ClientSideMediumPassword(value)) {
21036 } else if (this.ClientSideWeakPassword(value)) {
21043 if (strength < 2) {
21044 //this.markInvalid(this.errors.TooWeak);
21045 this.errorMsg = this.errors.TooWeak;
21050 console.log('strength2: ' + strength);
21052 //var pm = this.trigger.child('div/div/div').dom;
21054 var pm = this.trigger.child('div/div');
21055 pm.removeClass(this.meterClass);
21056 pm.addClass(this.meterClass[strength]);
21058 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21060 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21062 this.errorMsg = '';
21066 CharacterSetChecks: function (type)
21069 this.fResult = false;
21072 isctype: function (character, type)
21075 case this.kCapitalLetter:
21076 if (character >= 'A' && character <= 'Z') {
21081 case this.kSmallLetter:
21082 if (character >= 'a' && character <= 'z') {
21088 if (character >= '0' && character <= '9') {
21093 case this.kPunctuation:
21094 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21105 IsLongEnough: function (pwd, size)
21107 return !(pwd == null || isNaN(size) || pwd.length < size);
21110 SpansEnoughCharacterSets: function (word, nb)
21112 if (!this.IsLongEnough(word, nb))
21117 var characterSetChecks = new Array(
21118 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21119 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21122 for (var index = 0; index < word.length; ++index) {
21123 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21124 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21125 characterSetChecks[nCharSet].fResult = true;
21132 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21133 if (characterSetChecks[nCharSet].fResult) {
21138 if (nCharSets < nb) {
21144 ClientSideStrongPassword: function (pwd)
21146 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21149 ClientSideMediumPassword: function (pwd)
21151 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21154 ClientSideWeakPassword: function (pwd)
21156 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21159 })//<script type="text/javascript">
21162 * Based Ext JS Library 1.1.1
21163 * Copyright(c) 2006-2007, Ext JS, LLC.
21169 * @class Roo.HtmlEditorCore
21170 * @extends Roo.Component
21171 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21173 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21176 Roo.HtmlEditorCore = function(config){
21179 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21184 * @event initialize
21185 * Fires when the editor is fully initialized (including the iframe)
21186 * @param {Roo.HtmlEditorCore} this
21191 * Fires when the editor is first receives the focus. Any insertion must wait
21192 * until after this event.
21193 * @param {Roo.HtmlEditorCore} this
21197 * @event beforesync
21198 * Fires before the textarea is updated with content from the editor iframe. Return false
21199 * to cancel the sync.
21200 * @param {Roo.HtmlEditorCore} this
21201 * @param {String} html
21205 * @event beforepush
21206 * Fires before the iframe editor is updated with content from the textarea. Return false
21207 * to cancel the push.
21208 * @param {Roo.HtmlEditorCore} this
21209 * @param {String} html
21214 * Fires when the textarea is updated with content from the editor iframe.
21215 * @param {Roo.HtmlEditorCore} this
21216 * @param {String} html
21221 * Fires when the iframe editor is updated with content from the textarea.
21222 * @param {Roo.HtmlEditorCore} this
21223 * @param {String} html
21228 * @event editorevent
21229 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21230 * @param {Roo.HtmlEditorCore} this
21236 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21238 // defaults : white / black...
21239 this.applyBlacklists();
21246 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21250 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21256 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21261 * @cfg {Number} height (in pixels)
21265 * @cfg {Number} width (in pixels)
21270 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21273 stylesheets: false,
21278 // private properties
21279 validationEvent : false,
21281 initialized : false,
21283 sourceEditMode : false,
21284 onFocus : Roo.emptyFn,
21286 hideMode:'offsets',
21290 // blacklist + whitelisted elements..
21297 * Protected method that will not generally be called directly. It
21298 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21299 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21301 getDocMarkup : function(){
21305 // inherit styels from page...??
21306 if (this.stylesheets === false) {
21308 Roo.get(document.head).select('style').each(function(node) {
21309 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21312 Roo.get(document.head).select('link').each(function(node) {
21313 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21316 } else if (!this.stylesheets.length) {
21318 st = '<style type="text/css">' +
21319 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21322 st = '<style type="text/css">' +
21327 st += '<style type="text/css">' +
21328 'IMG { cursor: pointer } ' +
21331 var cls = 'roo-htmleditor-body';
21333 if(this.bodyCls.length){
21334 cls += ' ' + this.bodyCls;
21337 return '<html><head>' + st +
21338 //<style type="text/css">' +
21339 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21341 ' </head><body class="' + cls + '"></body></html>';
21345 onRender : function(ct, position)
21348 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21349 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21352 this.el.dom.style.border = '0 none';
21353 this.el.dom.setAttribute('tabIndex', -1);
21354 this.el.addClass('x-hidden hide');
21358 if(Roo.isIE){ // fix IE 1px bogus margin
21359 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21363 this.frameId = Roo.id();
21367 var iframe = this.owner.wrap.createChild({
21369 cls: 'form-control', // bootstrap..
21371 name: this.frameId,
21372 frameBorder : 'no',
21373 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21378 this.iframe = iframe.dom;
21380 this.assignDocWin();
21382 this.doc.designMode = 'on';
21385 this.doc.write(this.getDocMarkup());
21389 var task = { // must defer to wait for browser to be ready
21391 //console.log("run task?" + this.doc.readyState);
21392 this.assignDocWin();
21393 if(this.doc.body || this.doc.readyState == 'complete'){
21395 this.doc.designMode="on";
21399 Roo.TaskMgr.stop(task);
21400 this.initEditor.defer(10, this);
21407 Roo.TaskMgr.start(task);
21412 onResize : function(w, h)
21414 Roo.log('resize: ' +w + ',' + h );
21415 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21419 if(typeof w == 'number'){
21421 this.iframe.style.width = w + 'px';
21423 if(typeof h == 'number'){
21425 this.iframe.style.height = h + 'px';
21427 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21434 * Toggles the editor between standard and source edit mode.
21435 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21437 toggleSourceEdit : function(sourceEditMode){
21439 this.sourceEditMode = sourceEditMode === true;
21441 if(this.sourceEditMode){
21443 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21446 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21447 //this.iframe.className = '';
21450 //this.setSize(this.owner.wrap.getSize());
21451 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21458 * Protected method that will not generally be called directly. If you need/want
21459 * custom HTML cleanup, this is the method you should override.
21460 * @param {String} html The HTML to be cleaned
21461 * return {String} The cleaned HTML
21463 cleanHtml : function(html){
21464 html = String(html);
21465 if(html.length > 5){
21466 if(Roo.isSafari){ // strip safari nonsense
21467 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21470 if(html == ' '){
21477 * HTML Editor -> Textarea
21478 * Protected method that will not generally be called directly. Syncs the contents
21479 * of the editor iframe with the textarea.
21481 syncValue : function(){
21482 if(this.initialized){
21483 var bd = (this.doc.body || this.doc.documentElement);
21484 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21485 var html = bd.innerHTML;
21487 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21488 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21490 html = '<div style="'+m[0]+'">' + html + '</div>';
21493 html = this.cleanHtml(html);
21494 // fix up the special chars.. normaly like back quotes in word...
21495 // however we do not want to do this with chinese..
21496 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21497 var cc = b.charCodeAt();
21499 (cc >= 0x4E00 && cc < 0xA000 ) ||
21500 (cc >= 0x3400 && cc < 0x4E00 ) ||
21501 (cc >= 0xf900 && cc < 0xfb00 )
21507 if(this.owner.fireEvent('beforesync', this, html) !== false){
21508 this.el.dom.value = html;
21509 this.owner.fireEvent('sync', this, html);
21515 * Protected method that will not generally be called directly. Pushes the value of the textarea
21516 * into the iframe editor.
21518 pushValue : function(){
21519 if(this.initialized){
21520 var v = this.el.dom.value.trim();
21522 // if(v.length < 1){
21526 if(this.owner.fireEvent('beforepush', this, v) !== false){
21527 var d = (this.doc.body || this.doc.documentElement);
21529 this.cleanUpPaste();
21530 this.el.dom.value = d.innerHTML;
21531 this.owner.fireEvent('push', this, v);
21537 deferFocus : function(){
21538 this.focus.defer(10, this);
21542 focus : function(){
21543 if(this.win && !this.sourceEditMode){
21550 assignDocWin: function()
21552 var iframe = this.iframe;
21555 this.doc = iframe.contentWindow.document;
21556 this.win = iframe.contentWindow;
21558 // if (!Roo.get(this.frameId)) {
21561 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21562 // this.win = Roo.get(this.frameId).dom.contentWindow;
21564 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21568 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21569 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21574 initEditor : function(){
21575 //console.log("INIT EDITOR");
21576 this.assignDocWin();
21580 this.doc.designMode="on";
21582 this.doc.write(this.getDocMarkup());
21585 var dbody = (this.doc.body || this.doc.documentElement);
21586 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21587 // this copies styles from the containing element into thsi one..
21588 // not sure why we need all of this..
21589 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21591 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21592 //ss['background-attachment'] = 'fixed'; // w3c
21593 dbody.bgProperties = 'fixed'; // ie
21594 //Roo.DomHelper.applyStyles(dbody, ss);
21595 Roo.EventManager.on(this.doc, {
21596 //'mousedown': this.onEditorEvent,
21597 'mouseup': this.onEditorEvent,
21598 'dblclick': this.onEditorEvent,
21599 'click': this.onEditorEvent,
21600 'keyup': this.onEditorEvent,
21605 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21607 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21608 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21610 this.initialized = true;
21612 this.owner.fireEvent('initialize', this);
21617 onDestroy : function(){
21623 //for (var i =0; i < this.toolbars.length;i++) {
21624 // // fixme - ask toolbars for heights?
21625 // this.toolbars[i].onDestroy();
21628 //this.wrap.dom.innerHTML = '';
21629 //this.wrap.remove();
21634 onFirstFocus : function(){
21636 this.assignDocWin();
21639 this.activated = true;
21642 if(Roo.isGecko){ // prevent silly gecko errors
21644 var s = this.win.getSelection();
21645 if(!s.focusNode || s.focusNode.nodeType != 3){
21646 var r = s.getRangeAt(0);
21647 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21652 this.execCmd('useCSS', true);
21653 this.execCmd('styleWithCSS', false);
21656 this.owner.fireEvent('activate', this);
21660 adjustFont: function(btn){
21661 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21662 //if(Roo.isSafari){ // safari
21665 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21666 if(Roo.isSafari){ // safari
21667 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21668 v = (v < 10) ? 10 : v;
21669 v = (v > 48) ? 48 : v;
21670 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21675 v = Math.max(1, v+adjust);
21677 this.execCmd('FontSize', v );
21680 onEditorEvent : function(e)
21682 this.owner.fireEvent('editorevent', this, e);
21683 // this.updateToolbar();
21684 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21687 insertTag : function(tg)
21689 // could be a bit smarter... -> wrap the current selected tRoo..
21690 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21692 range = this.createRange(this.getSelection());
21693 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21694 wrappingNode.appendChild(range.extractContents());
21695 range.insertNode(wrappingNode);
21702 this.execCmd("formatblock", tg);
21706 insertText : function(txt)
21710 var range = this.createRange();
21711 range.deleteContents();
21712 //alert(Sender.getAttribute('label'));
21714 range.insertNode(this.doc.createTextNode(txt));
21720 * Executes a Midas editor command on the editor document and performs necessary focus and
21721 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21722 * @param {String} cmd The Midas command
21723 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21725 relayCmd : function(cmd, value){
21727 this.execCmd(cmd, value);
21728 this.owner.fireEvent('editorevent', this);
21729 //this.updateToolbar();
21730 this.owner.deferFocus();
21734 * Executes a Midas editor command directly on the editor document.
21735 * For visual commands, you should use {@link #relayCmd} instead.
21736 * <b>This should only be called after the editor is initialized.</b>
21737 * @param {String} cmd The Midas command
21738 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21740 execCmd : function(cmd, value){
21741 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21748 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21750 * @param {String} text | dom node..
21752 insertAtCursor : function(text)
21755 if(!this.activated){
21761 var r = this.doc.selection.createRange();
21772 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21776 // from jquery ui (MIT licenced)
21778 var win = this.win;
21780 if (win.getSelection && win.getSelection().getRangeAt) {
21781 range = win.getSelection().getRangeAt(0);
21782 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21783 range.insertNode(node);
21784 } else if (win.document.selection && win.document.selection.createRange) {
21785 // no firefox support
21786 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21787 win.document.selection.createRange().pasteHTML(txt);
21789 // no firefox support
21790 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21791 this.execCmd('InsertHTML', txt);
21800 mozKeyPress : function(e){
21802 var c = e.getCharCode(), cmd;
21805 c = String.fromCharCode(c).toLowerCase();
21819 this.cleanUpPaste.defer(100, this);
21827 e.preventDefault();
21835 fixKeys : function(){ // load time branching for fastest keydown performance
21837 return function(e){
21838 var k = e.getKey(), r;
21841 r = this.doc.selection.createRange();
21844 r.pasteHTML('    ');
21851 r = this.doc.selection.createRange();
21853 var target = r.parentElement();
21854 if(!target || target.tagName.toLowerCase() != 'li'){
21856 r.pasteHTML('<br />');
21862 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21863 this.cleanUpPaste.defer(100, this);
21869 }else if(Roo.isOpera){
21870 return function(e){
21871 var k = e.getKey();
21875 this.execCmd('InsertHTML','    ');
21878 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21879 this.cleanUpPaste.defer(100, this);
21884 }else if(Roo.isSafari){
21885 return function(e){
21886 var k = e.getKey();
21890 this.execCmd('InsertText','\t');
21894 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21895 this.cleanUpPaste.defer(100, this);
21903 getAllAncestors: function()
21905 var p = this.getSelectedNode();
21908 a.push(p); // push blank onto stack..
21909 p = this.getParentElement();
21913 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21917 a.push(this.doc.body);
21921 lastSelNode : false,
21924 getSelection : function()
21926 this.assignDocWin();
21927 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21930 getSelectedNode: function()
21932 // this may only work on Gecko!!!
21934 // should we cache this!!!!
21939 var range = this.createRange(this.getSelection()).cloneRange();
21942 var parent = range.parentElement();
21944 var testRange = range.duplicate();
21945 testRange.moveToElementText(parent);
21946 if (testRange.inRange(range)) {
21949 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21952 parent = parent.parentElement;
21957 // is ancestor a text element.
21958 var ac = range.commonAncestorContainer;
21959 if (ac.nodeType == 3) {
21960 ac = ac.parentNode;
21963 var ar = ac.childNodes;
21966 var other_nodes = [];
21967 var has_other_nodes = false;
21968 for (var i=0;i<ar.length;i++) {
21969 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21972 // fullly contained node.
21974 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21979 // probably selected..
21980 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21981 other_nodes.push(ar[i]);
21985 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21990 has_other_nodes = true;
21992 if (!nodes.length && other_nodes.length) {
21993 nodes= other_nodes;
21995 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22001 createRange: function(sel)
22003 // this has strange effects when using with
22004 // top toolbar - not sure if it's a great idea.
22005 //this.editor.contentWindow.focus();
22006 if (typeof sel != "undefined") {
22008 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22010 return this.doc.createRange();
22013 return this.doc.createRange();
22016 getParentElement: function()
22019 this.assignDocWin();
22020 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22022 var range = this.createRange(sel);
22025 var p = range.commonAncestorContainer;
22026 while (p.nodeType == 3) { // text node
22037 * Range intersection.. the hard stuff...
22041 * [ -- selected range --- ]
22045 * if end is before start or hits it. fail.
22046 * if start is after end or hits it fail.
22048 * if either hits (but other is outside. - then it's not
22054 // @see http://www.thismuchiknow.co.uk/?p=64.
22055 rangeIntersectsNode : function(range, node)
22057 var nodeRange = node.ownerDocument.createRange();
22059 nodeRange.selectNode(node);
22061 nodeRange.selectNodeContents(node);
22064 var rangeStartRange = range.cloneRange();
22065 rangeStartRange.collapse(true);
22067 var rangeEndRange = range.cloneRange();
22068 rangeEndRange.collapse(false);
22070 var nodeStartRange = nodeRange.cloneRange();
22071 nodeStartRange.collapse(true);
22073 var nodeEndRange = nodeRange.cloneRange();
22074 nodeEndRange.collapse(false);
22076 return rangeStartRange.compareBoundaryPoints(
22077 Range.START_TO_START, nodeEndRange) == -1 &&
22078 rangeEndRange.compareBoundaryPoints(
22079 Range.START_TO_START, nodeStartRange) == 1;
22083 rangeCompareNode : function(range, node)
22085 var nodeRange = node.ownerDocument.createRange();
22087 nodeRange.selectNode(node);
22089 nodeRange.selectNodeContents(node);
22093 range.collapse(true);
22095 nodeRange.collapse(true);
22097 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22098 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22100 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22102 var nodeIsBefore = ss == 1;
22103 var nodeIsAfter = ee == -1;
22105 if (nodeIsBefore && nodeIsAfter) {
22108 if (!nodeIsBefore && nodeIsAfter) {
22109 return 1; //right trailed.
22112 if (nodeIsBefore && !nodeIsAfter) {
22113 return 2; // left trailed.
22119 // private? - in a new class?
22120 cleanUpPaste : function()
22122 // cleans up the whole document..
22123 Roo.log('cleanuppaste');
22125 this.cleanUpChildren(this.doc.body);
22126 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22127 if (clean != this.doc.body.innerHTML) {
22128 this.doc.body.innerHTML = clean;
22133 cleanWordChars : function(input) {// change the chars to hex code
22134 var he = Roo.HtmlEditorCore;
22136 var output = input;
22137 Roo.each(he.swapCodes, function(sw) {
22138 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22140 output = output.replace(swapper, sw[1]);
22147 cleanUpChildren : function (n)
22149 if (!n.childNodes.length) {
22152 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22153 this.cleanUpChild(n.childNodes[i]);
22160 cleanUpChild : function (node)
22163 //console.log(node);
22164 if (node.nodeName == "#text") {
22165 // clean up silly Windows -- stuff?
22168 if (node.nodeName == "#comment") {
22169 node.parentNode.removeChild(node);
22170 // clean up silly Windows -- stuff?
22173 var lcname = node.tagName.toLowerCase();
22174 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22175 // whitelist of tags..
22177 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22179 node.parentNode.removeChild(node);
22184 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22186 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22187 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22189 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22190 // remove_keep_children = true;
22193 if (remove_keep_children) {
22194 this.cleanUpChildren(node);
22195 // inserts everything just before this node...
22196 while (node.childNodes.length) {
22197 var cn = node.childNodes[0];
22198 node.removeChild(cn);
22199 node.parentNode.insertBefore(cn, node);
22201 node.parentNode.removeChild(node);
22205 if (!node.attributes || !node.attributes.length) {
22206 this.cleanUpChildren(node);
22210 function cleanAttr(n,v)
22213 if (v.match(/^\./) || v.match(/^\//)) {
22216 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22219 if (v.match(/^#/)) {
22222 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22223 node.removeAttribute(n);
22227 var cwhite = this.cwhite;
22228 var cblack = this.cblack;
22230 function cleanStyle(n,v)
22232 if (v.match(/expression/)) { //XSS?? should we even bother..
22233 node.removeAttribute(n);
22237 var parts = v.split(/;/);
22240 Roo.each(parts, function(p) {
22241 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22245 var l = p.split(':').shift().replace(/\s+/g,'');
22246 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22248 if ( cwhite.length && cblack.indexOf(l) > -1) {
22249 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22250 //node.removeAttribute(n);
22254 // only allow 'c whitelisted system attributes'
22255 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22256 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22257 //node.removeAttribute(n);
22267 if (clean.length) {
22268 node.setAttribute(n, clean.join(';'));
22270 node.removeAttribute(n);
22276 for (var i = node.attributes.length-1; i > -1 ; i--) {
22277 var a = node.attributes[i];
22280 if (a.name.toLowerCase().substr(0,2)=='on') {
22281 node.removeAttribute(a.name);
22284 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22285 node.removeAttribute(a.name);
22288 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22289 cleanAttr(a.name,a.value); // fixme..
22292 if (a.name == 'style') {
22293 cleanStyle(a.name,a.value);
22296 /// clean up MS crap..
22297 // tecnically this should be a list of valid class'es..
22300 if (a.name == 'class') {
22301 if (a.value.match(/^Mso/)) {
22302 node.className = '';
22305 if (a.value.match(/^body$/)) {
22306 node.className = '';
22317 this.cleanUpChildren(node);
22323 * Clean up MS wordisms...
22325 cleanWord : function(node)
22330 this.cleanWord(this.doc.body);
22333 if (node.nodeName == "#text") {
22334 // clean up silly Windows -- stuff?
22337 if (node.nodeName == "#comment") {
22338 node.parentNode.removeChild(node);
22339 // clean up silly Windows -- stuff?
22343 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22344 node.parentNode.removeChild(node);
22348 // remove - but keep children..
22349 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22350 while (node.childNodes.length) {
22351 var cn = node.childNodes[0];
22352 node.removeChild(cn);
22353 node.parentNode.insertBefore(cn, node);
22355 node.parentNode.removeChild(node);
22356 this.iterateChildren(node, this.cleanWord);
22360 if (node.className.length) {
22362 var cn = node.className.split(/\W+/);
22364 Roo.each(cn, function(cls) {
22365 if (cls.match(/Mso[a-zA-Z]+/)) {
22370 node.className = cna.length ? cna.join(' ') : '';
22372 node.removeAttribute("class");
22376 if (node.hasAttribute("lang")) {
22377 node.removeAttribute("lang");
22380 if (node.hasAttribute("style")) {
22382 var styles = node.getAttribute("style").split(";");
22384 Roo.each(styles, function(s) {
22385 if (!s.match(/:/)) {
22388 var kv = s.split(":");
22389 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22392 // what ever is left... we allow.
22395 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22396 if (!nstyle.length) {
22397 node.removeAttribute('style');
22400 this.iterateChildren(node, this.cleanWord);
22406 * iterateChildren of a Node, calling fn each time, using this as the scole..
22407 * @param {DomNode} node node to iterate children of.
22408 * @param {Function} fn method of this class to call on each item.
22410 iterateChildren : function(node, fn)
22412 if (!node.childNodes.length) {
22415 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22416 fn.call(this, node.childNodes[i])
22422 * cleanTableWidths.
22424 * Quite often pasting from word etc.. results in tables with column and widths.
22425 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22428 cleanTableWidths : function(node)
22433 this.cleanTableWidths(this.doc.body);
22438 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22441 Roo.log(node.tagName);
22442 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22443 this.iterateChildren(node, this.cleanTableWidths);
22446 if (node.hasAttribute('width')) {
22447 node.removeAttribute('width');
22451 if (node.hasAttribute("style")) {
22454 var styles = node.getAttribute("style").split(";");
22456 Roo.each(styles, function(s) {
22457 if (!s.match(/:/)) {
22460 var kv = s.split(":");
22461 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22464 // what ever is left... we allow.
22467 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22468 if (!nstyle.length) {
22469 node.removeAttribute('style');
22473 this.iterateChildren(node, this.cleanTableWidths);
22481 domToHTML : function(currentElement, depth, nopadtext) {
22483 depth = depth || 0;
22484 nopadtext = nopadtext || false;
22486 if (!currentElement) {
22487 return this.domToHTML(this.doc.body);
22490 //Roo.log(currentElement);
22492 var allText = false;
22493 var nodeName = currentElement.nodeName;
22494 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22496 if (nodeName == '#text') {
22498 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22503 if (nodeName != 'BODY') {
22506 // Prints the node tagName, such as <A>, <IMG>, etc
22509 for(i = 0; i < currentElement.attributes.length;i++) {
22511 var aname = currentElement.attributes.item(i).name;
22512 if (!currentElement.attributes.item(i).value.length) {
22515 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22518 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22527 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22530 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22535 // Traverse the tree
22537 var currentElementChild = currentElement.childNodes.item(i);
22538 var allText = true;
22539 var innerHTML = '';
22541 while (currentElementChild) {
22542 // Formatting code (indent the tree so it looks nice on the screen)
22543 var nopad = nopadtext;
22544 if (lastnode == 'SPAN') {
22548 if (currentElementChild.nodeName == '#text') {
22549 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22550 toadd = nopadtext ? toadd : toadd.trim();
22551 if (!nopad && toadd.length > 80) {
22552 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22554 innerHTML += toadd;
22557 currentElementChild = currentElement.childNodes.item(i);
22563 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22565 // Recursively traverse the tree structure of the child node
22566 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22567 lastnode = currentElementChild.nodeName;
22569 currentElementChild=currentElement.childNodes.item(i);
22575 // The remaining code is mostly for formatting the tree
22576 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22581 ret+= "</"+tagName+">";
22587 applyBlacklists : function()
22589 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22590 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22594 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22595 if (b.indexOf(tag) > -1) {
22598 this.white.push(tag);
22602 Roo.each(w, function(tag) {
22603 if (b.indexOf(tag) > -1) {
22606 if (this.white.indexOf(tag) > -1) {
22609 this.white.push(tag);
22614 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22615 if (w.indexOf(tag) > -1) {
22618 this.black.push(tag);
22622 Roo.each(b, function(tag) {
22623 if (w.indexOf(tag) > -1) {
22626 if (this.black.indexOf(tag) > -1) {
22629 this.black.push(tag);
22634 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22635 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22639 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22640 if (b.indexOf(tag) > -1) {
22643 this.cwhite.push(tag);
22647 Roo.each(w, function(tag) {
22648 if (b.indexOf(tag) > -1) {
22651 if (this.cwhite.indexOf(tag) > -1) {
22654 this.cwhite.push(tag);
22659 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22660 if (w.indexOf(tag) > -1) {
22663 this.cblack.push(tag);
22667 Roo.each(b, function(tag) {
22668 if (w.indexOf(tag) > -1) {
22671 if (this.cblack.indexOf(tag) > -1) {
22674 this.cblack.push(tag);
22679 setStylesheets : function(stylesheets)
22681 if(typeof(stylesheets) == 'string'){
22682 Roo.get(this.iframe.contentDocument.head).createChild({
22684 rel : 'stylesheet',
22693 Roo.each(stylesheets, function(s) {
22698 Roo.get(_this.iframe.contentDocument.head).createChild({
22700 rel : 'stylesheet',
22709 removeStylesheets : function()
22713 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22718 setStyle : function(style)
22720 Roo.get(this.iframe.contentDocument.head).createChild({
22729 // hide stuff that is not compatible
22743 * @event specialkey
22747 * @cfg {String} fieldClass @hide
22750 * @cfg {String} focusClass @hide
22753 * @cfg {String} autoCreate @hide
22756 * @cfg {String} inputType @hide
22759 * @cfg {String} invalidClass @hide
22762 * @cfg {String} invalidText @hide
22765 * @cfg {String} msgFx @hide
22768 * @cfg {String} validateOnBlur @hide
22772 Roo.HtmlEditorCore.white = [
22773 'area', 'br', 'img', 'input', 'hr', 'wbr',
22775 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22776 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22777 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22778 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22779 'table', 'ul', 'xmp',
22781 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22784 'dir', 'menu', 'ol', 'ul', 'dl',
22790 Roo.HtmlEditorCore.black = [
22791 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22793 'base', 'basefont', 'bgsound', 'blink', 'body',
22794 'frame', 'frameset', 'head', 'html', 'ilayer',
22795 'iframe', 'layer', 'link', 'meta', 'object',
22796 'script', 'style' ,'title', 'xml' // clean later..
22798 Roo.HtmlEditorCore.clean = [
22799 'script', 'style', 'title', 'xml'
22801 Roo.HtmlEditorCore.remove = [
22806 Roo.HtmlEditorCore.ablack = [
22810 Roo.HtmlEditorCore.aclean = [
22811 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22815 Roo.HtmlEditorCore.pwhite= [
22816 'http', 'https', 'mailto'
22819 // white listed style attributes.
22820 Roo.HtmlEditorCore.cwhite= [
22821 // 'text-align', /// default is to allow most things..
22827 // black listed style attributes.
22828 Roo.HtmlEditorCore.cblack= [
22829 // 'font-size' -- this can be set by the project
22833 Roo.HtmlEditorCore.swapCodes =[
22852 * @class Roo.bootstrap.HtmlEditor
22853 * @extends Roo.bootstrap.TextArea
22854 * Bootstrap HtmlEditor class
22857 * Create a new HtmlEditor
22858 * @param {Object} config The config object
22861 Roo.bootstrap.HtmlEditor = function(config){
22862 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22863 if (!this.toolbars) {
22864 this.toolbars = [];
22867 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22870 * @event initialize
22871 * Fires when the editor is fully initialized (including the iframe)
22872 * @param {HtmlEditor} this
22877 * Fires when the editor is first receives the focus. Any insertion must wait
22878 * until after this event.
22879 * @param {HtmlEditor} this
22883 * @event beforesync
22884 * Fires before the textarea is updated with content from the editor iframe. Return false
22885 * to cancel the sync.
22886 * @param {HtmlEditor} this
22887 * @param {String} html
22891 * @event beforepush
22892 * Fires before the iframe editor is updated with content from the textarea. Return false
22893 * to cancel the push.
22894 * @param {HtmlEditor} this
22895 * @param {String} html
22900 * Fires when the textarea is updated with content from the editor iframe.
22901 * @param {HtmlEditor} this
22902 * @param {String} html
22907 * Fires when the iframe editor is updated with content from the textarea.
22908 * @param {HtmlEditor} this
22909 * @param {String} html
22913 * @event editmodechange
22914 * Fires when the editor switches edit modes
22915 * @param {HtmlEditor} this
22916 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22918 editmodechange: true,
22920 * @event editorevent
22921 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22922 * @param {HtmlEditor} this
22926 * @event firstfocus
22927 * Fires when on first focus - needed by toolbars..
22928 * @param {HtmlEditor} this
22933 * Auto save the htmlEditor value as a file into Events
22934 * @param {HtmlEditor} this
22938 * @event savedpreview
22939 * preview the saved version of htmlEditor
22940 * @param {HtmlEditor} this
22947 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22951 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22956 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22961 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22966 * @cfg {Number} height (in pixels)
22970 * @cfg {Number} width (in pixels)
22975 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22978 stylesheets: false,
22983 // private properties
22984 validationEvent : false,
22986 initialized : false,
22989 onFocus : Roo.emptyFn,
22991 hideMode:'offsets',
22993 tbContainer : false,
22997 toolbarContainer :function() {
22998 return this.wrap.select('.x-html-editor-tb',true).first();
23002 * Protected method that will not generally be called directly. It
23003 * is called when the editor creates its toolbar. Override this method if you need to
23004 * add custom toolbar buttons.
23005 * @param {HtmlEditor} editor
23007 createToolbar : function(){
23008 Roo.log('renewing');
23009 Roo.log("create toolbars");
23011 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23012 this.toolbars[0].render(this.toolbarContainer());
23016 // if (!editor.toolbars || !editor.toolbars.length) {
23017 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23020 // for (var i =0 ; i < editor.toolbars.length;i++) {
23021 // editor.toolbars[i] = Roo.factory(
23022 // typeof(editor.toolbars[i]) == 'string' ?
23023 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23024 // Roo.bootstrap.HtmlEditor);
23025 // editor.toolbars[i].init(editor);
23031 onRender : function(ct, position)
23033 // Roo.log("Call onRender: " + this.xtype);
23035 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23037 this.wrap = this.inputEl().wrap({
23038 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23041 this.editorcore.onRender(ct, position);
23043 if (this.resizable) {
23044 this.resizeEl = new Roo.Resizable(this.wrap, {
23048 minHeight : this.height,
23049 height: this.height,
23050 handles : this.resizable,
23053 resize : function(r, w, h) {
23054 _t.onResize(w,h); // -something
23060 this.createToolbar(this);
23063 if(!this.width && this.resizable){
23064 this.setSize(this.wrap.getSize());
23066 if (this.resizeEl) {
23067 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23068 // should trigger onReize..
23074 onResize : function(w, h)
23076 Roo.log('resize: ' +w + ',' + h );
23077 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23081 if(this.inputEl() ){
23082 if(typeof w == 'number'){
23083 var aw = w - this.wrap.getFrameWidth('lr');
23084 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23087 if(typeof h == 'number'){
23088 var tbh = -11; // fixme it needs to tool bar size!
23089 for (var i =0; i < this.toolbars.length;i++) {
23090 // fixme - ask toolbars for heights?
23091 tbh += this.toolbars[i].el.getHeight();
23092 //if (this.toolbars[i].footer) {
23093 // tbh += this.toolbars[i].footer.el.getHeight();
23101 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23102 ah -= 5; // knock a few pixes off for look..
23103 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23107 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23108 this.editorcore.onResize(ew,eh);
23113 * Toggles the editor between standard and source edit mode.
23114 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23116 toggleSourceEdit : function(sourceEditMode)
23118 this.editorcore.toggleSourceEdit(sourceEditMode);
23120 if(this.editorcore.sourceEditMode){
23121 Roo.log('editor - showing textarea');
23124 // Roo.log(this.syncValue());
23126 this.inputEl().removeClass(['hide', 'x-hidden']);
23127 this.inputEl().dom.removeAttribute('tabIndex');
23128 this.inputEl().focus();
23130 Roo.log('editor - hiding textarea');
23132 // Roo.log(this.pushValue());
23135 this.inputEl().addClass(['hide', 'x-hidden']);
23136 this.inputEl().dom.setAttribute('tabIndex', -1);
23137 //this.deferFocus();
23140 if(this.resizable){
23141 this.setSize(this.wrap.getSize());
23144 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23147 // private (for BoxComponent)
23148 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23150 // private (for BoxComponent)
23151 getResizeEl : function(){
23155 // private (for BoxComponent)
23156 getPositionEl : function(){
23161 initEvents : function(){
23162 this.originalValue = this.getValue();
23166 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23169 // markInvalid : Roo.emptyFn,
23171 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23174 // clearInvalid : Roo.emptyFn,
23176 setValue : function(v){
23177 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23178 this.editorcore.pushValue();
23183 deferFocus : function(){
23184 this.focus.defer(10, this);
23188 focus : function(){
23189 this.editorcore.focus();
23195 onDestroy : function(){
23201 for (var i =0; i < this.toolbars.length;i++) {
23202 // fixme - ask toolbars for heights?
23203 this.toolbars[i].onDestroy();
23206 this.wrap.dom.innerHTML = '';
23207 this.wrap.remove();
23212 onFirstFocus : function(){
23213 //Roo.log("onFirstFocus");
23214 this.editorcore.onFirstFocus();
23215 for (var i =0; i < this.toolbars.length;i++) {
23216 this.toolbars[i].onFirstFocus();
23222 syncValue : function()
23224 this.editorcore.syncValue();
23227 pushValue : function()
23229 this.editorcore.pushValue();
23233 // hide stuff that is not compatible
23247 * @event specialkey
23251 * @cfg {String} fieldClass @hide
23254 * @cfg {String} focusClass @hide
23257 * @cfg {String} autoCreate @hide
23260 * @cfg {String} inputType @hide
23263 * @cfg {String} invalidClass @hide
23266 * @cfg {String} invalidText @hide
23269 * @cfg {String} msgFx @hide
23272 * @cfg {String} validateOnBlur @hide
23281 Roo.namespace('Roo.bootstrap.htmleditor');
23283 * @class Roo.bootstrap.HtmlEditorToolbar1
23288 new Roo.bootstrap.HtmlEditor({
23291 new Roo.bootstrap.HtmlEditorToolbar1({
23292 disable : { fonts: 1 , format: 1, ..., ... , ...],
23298 * @cfg {Object} disable List of elements to disable..
23299 * @cfg {Array} btns List of additional buttons.
23303 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23306 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23309 Roo.apply(this, config);
23311 // default disabled, based on 'good practice'..
23312 this.disable = this.disable || {};
23313 Roo.applyIf(this.disable, {
23316 specialElements : true
23318 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23320 this.editor = config.editor;
23321 this.editorcore = config.editor.editorcore;
23323 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23325 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23326 // dont call parent... till later.
23328 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23333 editorcore : false,
23338 "h1","h2","h3","h4","h5","h6",
23340 "abbr", "acronym", "address", "cite", "samp", "var",
23344 onRender : function(ct, position)
23346 // Roo.log("Call onRender: " + this.xtype);
23348 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23350 this.el.dom.style.marginBottom = '0';
23352 var editorcore = this.editorcore;
23353 var editor= this.editor;
23356 var btn = function(id,cmd , toggle, handler, html){
23358 var event = toggle ? 'toggle' : 'click';
23363 xns: Roo.bootstrap,
23366 enableToggle:toggle !== false,
23368 pressed : toggle ? false : null,
23371 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23372 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23378 // var cb_box = function...
23383 xns: Roo.bootstrap,
23384 glyphicon : 'font',
23388 xns: Roo.bootstrap,
23392 Roo.each(this.formats, function(f) {
23393 style.menu.items.push({
23395 xns: Roo.bootstrap,
23396 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23401 editorcore.insertTag(this.tagname);
23408 children.push(style);
23410 btn('bold',false,true);
23411 btn('italic',false,true);
23412 btn('align-left', 'justifyleft',true);
23413 btn('align-center', 'justifycenter',true);
23414 btn('align-right' , 'justifyright',true);
23415 btn('link', false, false, function(btn) {
23416 //Roo.log("create link?");
23417 var url = prompt(this.createLinkText, this.defaultLinkValue);
23418 if(url && url != 'http:/'+'/'){
23419 this.editorcore.relayCmd('createlink', url);
23422 btn('list','insertunorderedlist',true);
23423 btn('pencil', false,true, function(btn){
23425 this.toggleSourceEdit(btn.pressed);
23428 if (this.editor.btns.length > 0) {
23429 for (var i = 0; i<this.editor.btns.length; i++) {
23430 children.push(this.editor.btns[i]);
23438 xns: Roo.bootstrap,
23443 xns: Roo.bootstrap,
23448 cog.menu.items.push({
23450 xns: Roo.bootstrap,
23451 html : Clean styles,
23456 editorcore.insertTag(this.tagname);
23465 this.xtype = 'NavSimplebar';
23467 for(var i=0;i< children.length;i++) {
23469 this.buttons.add(this.addxtypeChild(children[i]));
23473 editor.on('editorevent', this.updateToolbar, this);
23475 onBtnClick : function(id)
23477 this.editorcore.relayCmd(id);
23478 this.editorcore.focus();
23482 * Protected method that will not generally be called directly. It triggers
23483 * a toolbar update by reading the markup state of the current selection in the editor.
23485 updateToolbar: function(){
23487 if(!this.editorcore.activated){
23488 this.editor.onFirstFocus(); // is this neeed?
23492 var btns = this.buttons;
23493 var doc = this.editorcore.doc;
23494 btns.get('bold').setActive(doc.queryCommandState('bold'));
23495 btns.get('italic').setActive(doc.queryCommandState('italic'));
23496 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23498 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23499 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23500 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23502 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23503 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23506 var ans = this.editorcore.getAllAncestors();
23507 if (this.formatCombo) {
23510 var store = this.formatCombo.store;
23511 this.formatCombo.setValue("");
23512 for (var i =0; i < ans.length;i++) {
23513 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23515 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23523 // hides menus... - so this cant be on a menu...
23524 Roo.bootstrap.MenuMgr.hideAll();
23526 Roo.bootstrap.MenuMgr.hideAll();
23527 //this.editorsyncValue();
23529 onFirstFocus: function() {
23530 this.buttons.each(function(item){
23534 toggleSourceEdit : function(sourceEditMode){
23537 if(sourceEditMode){
23538 Roo.log("disabling buttons");
23539 this.buttons.each( function(item){
23540 if(item.cmd != 'pencil'){
23546 Roo.log("enabling buttons");
23547 if(this.editorcore.initialized){
23548 this.buttons.each( function(item){
23554 Roo.log("calling toggole on editor");
23555 // tell the editor that it's been pressed..
23556 this.editor.toggleSourceEdit(sourceEditMode);
23566 * @class Roo.bootstrap.Table.AbstractSelectionModel
23567 * @extends Roo.util.Observable
23568 * Abstract base class for grid SelectionModels. It provides the interface that should be
23569 * implemented by descendant classes. This class should not be directly instantiated.
23572 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23573 this.locked = false;
23574 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23578 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23579 /** @ignore Called by the grid automatically. Do not call directly. */
23580 init : function(grid){
23586 * Locks the selections.
23589 this.locked = true;
23593 * Unlocks the selections.
23595 unlock : function(){
23596 this.locked = false;
23600 * Returns true if the selections are locked.
23601 * @return {Boolean}
23603 isLocked : function(){
23604 return this.locked;
23608 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23609 * @class Roo.bootstrap.Table.RowSelectionModel
23610 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23611 * It supports multiple selections and keyboard selection/navigation.
23613 * @param {Object} config
23616 Roo.bootstrap.Table.RowSelectionModel = function(config){
23617 Roo.apply(this, config);
23618 this.selections = new Roo.util.MixedCollection(false, function(o){
23623 this.lastActive = false;
23627 * @event selectionchange
23628 * Fires when the selection changes
23629 * @param {SelectionModel} this
23631 "selectionchange" : true,
23633 * @event afterselectionchange
23634 * Fires after the selection changes (eg. by key press or clicking)
23635 * @param {SelectionModel} this
23637 "afterselectionchange" : true,
23639 * @event beforerowselect
23640 * Fires when a row is selected being selected, return false to cancel.
23641 * @param {SelectionModel} this
23642 * @param {Number} rowIndex The selected index
23643 * @param {Boolean} keepExisting False if other selections will be cleared
23645 "beforerowselect" : true,
23648 * Fires when a row is selected.
23649 * @param {SelectionModel} this
23650 * @param {Number} rowIndex The selected index
23651 * @param {Roo.data.Record} r The record
23653 "rowselect" : true,
23655 * @event rowdeselect
23656 * Fires when a row is deselected.
23657 * @param {SelectionModel} this
23658 * @param {Number} rowIndex The selected index
23660 "rowdeselect" : true
23662 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23663 this.locked = false;
23666 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23668 * @cfg {Boolean} singleSelect
23669 * True to allow selection of only one row at a time (defaults to false)
23671 singleSelect : false,
23674 initEvents : function()
23677 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23678 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23679 //}else{ // allow click to work like normal
23680 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23682 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23683 this.grid.on("rowclick", this.handleMouseDown, this);
23685 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23686 "up" : function(e){
23688 this.selectPrevious(e.shiftKey);
23689 }else if(this.last !== false && this.lastActive !== false){
23690 var last = this.last;
23691 this.selectRange(this.last, this.lastActive-1);
23692 this.grid.getView().focusRow(this.lastActive);
23693 if(last !== false){
23697 this.selectFirstRow();
23699 this.fireEvent("afterselectionchange", this);
23701 "down" : function(e){
23703 this.selectNext(e.shiftKey);
23704 }else if(this.last !== false && this.lastActive !== false){
23705 var last = this.last;
23706 this.selectRange(this.last, this.lastActive+1);
23707 this.grid.getView().focusRow(this.lastActive);
23708 if(last !== false){
23712 this.selectFirstRow();
23714 this.fireEvent("afterselectionchange", this);
23718 this.grid.store.on('load', function(){
23719 this.selections.clear();
23722 var view = this.grid.view;
23723 view.on("refresh", this.onRefresh, this);
23724 view.on("rowupdated", this.onRowUpdated, this);
23725 view.on("rowremoved", this.onRemove, this);
23730 onRefresh : function()
23732 var ds = this.grid.store, i, v = this.grid.view;
23733 var s = this.selections;
23734 s.each(function(r){
23735 if((i = ds.indexOfId(r.id)) != -1){
23744 onRemove : function(v, index, r){
23745 this.selections.remove(r);
23749 onRowUpdated : function(v, index, r){
23750 if(this.isSelected(r)){
23751 v.onRowSelect(index);
23757 * @param {Array} records The records to select
23758 * @param {Boolean} keepExisting (optional) True to keep existing selections
23760 selectRecords : function(records, keepExisting)
23763 this.clearSelections();
23765 var ds = this.grid.store;
23766 for(var i = 0, len = records.length; i < len; i++){
23767 this.selectRow(ds.indexOf(records[i]), true);
23772 * Gets the number of selected rows.
23775 getCount : function(){
23776 return this.selections.length;
23780 * Selects the first row in the grid.
23782 selectFirstRow : function(){
23787 * Select the last row.
23788 * @param {Boolean} keepExisting (optional) True to keep existing selections
23790 selectLastRow : function(keepExisting){
23791 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23792 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23796 * Selects the row immediately following the last selected row.
23797 * @param {Boolean} keepExisting (optional) True to keep existing selections
23799 selectNext : function(keepExisting)
23801 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23802 this.selectRow(this.last+1, keepExisting);
23803 this.grid.getView().focusRow(this.last);
23808 * Selects the row that precedes the last selected row.
23809 * @param {Boolean} keepExisting (optional) True to keep existing selections
23811 selectPrevious : function(keepExisting){
23813 this.selectRow(this.last-1, keepExisting);
23814 this.grid.getView().focusRow(this.last);
23819 * Returns the selected records
23820 * @return {Array} Array of selected records
23822 getSelections : function(){
23823 return [].concat(this.selections.items);
23827 * Returns the first selected record.
23830 getSelected : function(){
23831 return this.selections.itemAt(0);
23836 * Clears all selections.
23838 clearSelections : function(fast)
23844 var ds = this.grid.store;
23845 var s = this.selections;
23846 s.each(function(r){
23847 this.deselectRow(ds.indexOfId(r.id));
23851 this.selections.clear();
23858 * Selects all rows.
23860 selectAll : function(){
23864 this.selections.clear();
23865 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23866 this.selectRow(i, true);
23871 * Returns True if there is a selection.
23872 * @return {Boolean}
23874 hasSelection : function(){
23875 return this.selections.length > 0;
23879 * Returns True if the specified row is selected.
23880 * @param {Number/Record} record The record or index of the record to check
23881 * @return {Boolean}
23883 isSelected : function(index){
23884 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23885 return (r && this.selections.key(r.id) ? true : false);
23889 * Returns True if the specified record id is selected.
23890 * @param {String} id The id of record to check
23891 * @return {Boolean}
23893 isIdSelected : function(id){
23894 return (this.selections.key(id) ? true : false);
23899 handleMouseDBClick : function(e, t){
23903 handleMouseDown : function(e, t)
23905 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23906 if(this.isLocked() || rowIndex < 0 ){
23909 if(e.shiftKey && this.last !== false){
23910 var last = this.last;
23911 this.selectRange(last, rowIndex, e.ctrlKey);
23912 this.last = last; // reset the last
23916 var isSelected = this.isSelected(rowIndex);
23917 //Roo.log("select row:" + rowIndex);
23919 this.deselectRow(rowIndex);
23921 this.selectRow(rowIndex, true);
23925 if(e.button !== 0 && isSelected){
23926 alert('rowIndex 2: ' + rowIndex);
23927 view.focusRow(rowIndex);
23928 }else if(e.ctrlKey && isSelected){
23929 this.deselectRow(rowIndex);
23930 }else if(!isSelected){
23931 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23932 view.focusRow(rowIndex);
23936 this.fireEvent("afterselectionchange", this);
23939 handleDragableRowClick : function(grid, rowIndex, e)
23941 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23942 this.selectRow(rowIndex, false);
23943 grid.view.focusRow(rowIndex);
23944 this.fireEvent("afterselectionchange", this);
23949 * Selects multiple rows.
23950 * @param {Array} rows Array of the indexes of the row to select
23951 * @param {Boolean} keepExisting (optional) True to keep existing selections
23953 selectRows : function(rows, keepExisting){
23955 this.clearSelections();
23957 for(var i = 0, len = rows.length; i < len; i++){
23958 this.selectRow(rows[i], true);
23963 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23964 * @param {Number} startRow The index of the first row in the range
23965 * @param {Number} endRow The index of the last row in the range
23966 * @param {Boolean} keepExisting (optional) True to retain existing selections
23968 selectRange : function(startRow, endRow, keepExisting){
23973 this.clearSelections();
23975 if(startRow <= endRow){
23976 for(var i = startRow; i <= endRow; i++){
23977 this.selectRow(i, true);
23980 for(var i = startRow; i >= endRow; i--){
23981 this.selectRow(i, true);
23987 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23988 * @param {Number} startRow The index of the first row in the range
23989 * @param {Number} endRow The index of the last row in the range
23991 deselectRange : function(startRow, endRow, preventViewNotify){
23995 for(var i = startRow; i <= endRow; i++){
23996 this.deselectRow(i, preventViewNotify);
24002 * @param {Number} row The index of the row to select
24003 * @param {Boolean} keepExisting (optional) True to keep existing selections
24005 selectRow : function(index, keepExisting, preventViewNotify)
24007 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24010 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24011 if(!keepExisting || this.singleSelect){
24012 this.clearSelections();
24015 var r = this.grid.store.getAt(index);
24016 //console.log('selectRow - record id :' + r.id);
24018 this.selections.add(r);
24019 this.last = this.lastActive = index;
24020 if(!preventViewNotify){
24021 var proxy = new Roo.Element(
24022 this.grid.getRowDom(index)
24024 proxy.addClass('bg-info info');
24026 this.fireEvent("rowselect", this, index, r);
24027 this.fireEvent("selectionchange", this);
24033 * @param {Number} row The index of the row to deselect
24035 deselectRow : function(index, preventViewNotify)
24040 if(this.last == index){
24043 if(this.lastActive == index){
24044 this.lastActive = false;
24047 var r = this.grid.store.getAt(index);
24052 this.selections.remove(r);
24053 //.console.log('deselectRow - record id :' + r.id);
24054 if(!preventViewNotify){
24056 var proxy = new Roo.Element(
24057 this.grid.getRowDom(index)
24059 proxy.removeClass('bg-info info');
24061 this.fireEvent("rowdeselect", this, index);
24062 this.fireEvent("selectionchange", this);
24066 restoreLast : function(){
24068 this.last = this._last;
24073 acceptsNav : function(row, col, cm){
24074 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24078 onEditorKey : function(field, e){
24079 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24084 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24086 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24088 }else if(k == e.ENTER && !e.ctrlKey){
24092 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24094 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24096 }else if(k == e.ESC){
24100 g.startEditing(newCell[0], newCell[1]);
24106 * Ext JS Library 1.1.1
24107 * Copyright(c) 2006-2007, Ext JS, LLC.
24109 * Originally Released Under LGPL - original licence link has changed is not relivant.
24112 * <script type="text/javascript">
24116 * @class Roo.bootstrap.PagingToolbar
24117 * @extends Roo.bootstrap.NavSimplebar
24118 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24120 * Create a new PagingToolbar
24121 * @param {Object} config The config object
24122 * @param {Roo.data.Store} store
24124 Roo.bootstrap.PagingToolbar = function(config)
24126 // old args format still supported... - xtype is prefered..
24127 // created from xtype...
24129 this.ds = config.dataSource;
24131 if (config.store && !this.ds) {
24132 this.store= Roo.factory(config.store, Roo.data);
24133 this.ds = this.store;
24134 this.ds.xmodule = this.xmodule || false;
24137 this.toolbarItems = [];
24138 if (config.items) {
24139 this.toolbarItems = config.items;
24142 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24147 this.bind(this.ds);
24150 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24154 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24156 * @cfg {Roo.data.Store} dataSource
24157 * The underlying data store providing the paged data
24160 * @cfg {String/HTMLElement/Element} container
24161 * container The id or element that will contain the toolbar
24164 * @cfg {Boolean} displayInfo
24165 * True to display the displayMsg (defaults to false)
24168 * @cfg {Number} pageSize
24169 * The number of records to display per page (defaults to 20)
24173 * @cfg {String} displayMsg
24174 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24176 displayMsg : 'Displaying {0} - {1} of {2}',
24178 * @cfg {String} emptyMsg
24179 * The message to display when no records are found (defaults to "No data to display")
24181 emptyMsg : 'No data to display',
24183 * Customizable piece of the default paging text (defaults to "Page")
24186 beforePageText : "Page",
24188 * Customizable piece of the default paging text (defaults to "of %0")
24191 afterPageText : "of {0}",
24193 * Customizable piece of the default paging text (defaults to "First Page")
24196 firstText : "First Page",
24198 * Customizable piece of the default paging text (defaults to "Previous Page")
24201 prevText : "Previous Page",
24203 * Customizable piece of the default paging text (defaults to "Next Page")
24206 nextText : "Next Page",
24208 * Customizable piece of the default paging text (defaults to "Last Page")
24211 lastText : "Last Page",
24213 * Customizable piece of the default paging text (defaults to "Refresh")
24216 refreshText : "Refresh",
24220 onRender : function(ct, position)
24222 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24223 this.navgroup.parentId = this.id;
24224 this.navgroup.onRender(this.el, null);
24225 // add the buttons to the navgroup
24227 if(this.displayInfo){
24228 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24229 this.displayEl = this.el.select('.x-paging-info', true).first();
24230 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24231 // this.displayEl = navel.el.select('span',true).first();
24237 Roo.each(_this.buttons, function(e){ // this might need to use render????
24238 Roo.factory(e).onRender(_this.el, null);
24242 Roo.each(_this.toolbarItems, function(e) {
24243 _this.navgroup.addItem(e);
24247 this.first = this.navgroup.addItem({
24248 tooltip: this.firstText,
24250 icon : 'fa fa-backward',
24252 preventDefault: true,
24253 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24256 this.prev = this.navgroup.addItem({
24257 tooltip: this.prevText,
24259 icon : 'fa fa-step-backward',
24261 preventDefault: true,
24262 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24264 //this.addSeparator();
24267 var field = this.navgroup.addItem( {
24269 cls : 'x-paging-position',
24271 html : this.beforePageText +
24272 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24273 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24276 this.field = field.el.select('input', true).first();
24277 this.field.on("keydown", this.onPagingKeydown, this);
24278 this.field.on("focus", function(){this.dom.select();});
24281 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24282 //this.field.setHeight(18);
24283 //this.addSeparator();
24284 this.next = this.navgroup.addItem({
24285 tooltip: this.nextText,
24287 html : ' <i class="fa fa-step-forward">',
24289 preventDefault: true,
24290 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24292 this.last = this.navgroup.addItem({
24293 tooltip: this.lastText,
24294 icon : 'fa fa-forward',
24297 preventDefault: true,
24298 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24300 //this.addSeparator();
24301 this.loading = this.navgroup.addItem({
24302 tooltip: this.refreshText,
24303 icon: 'fa fa-refresh',
24304 preventDefault: true,
24305 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24311 updateInfo : function(){
24312 if(this.displayEl){
24313 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24314 var msg = count == 0 ?
24318 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24320 this.displayEl.update(msg);
24325 onLoad : function(ds, r, o)
24327 this.cursor = o.params ? o.params.start : 0;
24328 var d = this.getPageData(),
24333 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24334 this.field.dom.value = ap;
24335 this.first.setDisabled(ap == 1);
24336 this.prev.setDisabled(ap == 1);
24337 this.next.setDisabled(ap == ps);
24338 this.last.setDisabled(ap == ps);
24339 this.loading.enable();
24344 getPageData : function(){
24345 var total = this.ds.getTotalCount();
24348 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24349 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24354 onLoadError : function(){
24355 this.loading.enable();
24359 onPagingKeydown : function(e){
24360 var k = e.getKey();
24361 var d = this.getPageData();
24363 var v = this.field.dom.value, pageNum;
24364 if(!v || isNaN(pageNum = parseInt(v, 10))){
24365 this.field.dom.value = d.activePage;
24368 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24369 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24372 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))
24374 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24375 this.field.dom.value = pageNum;
24376 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24379 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24381 var v = this.field.dom.value, pageNum;
24382 var increment = (e.shiftKey) ? 10 : 1;
24383 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24386 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24387 this.field.dom.value = d.activePage;
24390 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24392 this.field.dom.value = parseInt(v, 10) + increment;
24393 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24394 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24401 beforeLoad : function(){
24403 this.loading.disable();
24408 onClick : function(which){
24417 ds.load({params:{start: 0, limit: this.pageSize}});
24420 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24423 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24426 var total = ds.getTotalCount();
24427 var extra = total % this.pageSize;
24428 var lastStart = extra ? (total - extra) : total-this.pageSize;
24429 ds.load({params:{start: lastStart, limit: this.pageSize}});
24432 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24438 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24439 * @param {Roo.data.Store} store The data store to unbind
24441 unbind : function(ds){
24442 ds.un("beforeload", this.beforeLoad, this);
24443 ds.un("load", this.onLoad, this);
24444 ds.un("loadexception", this.onLoadError, this);
24445 ds.un("remove", this.updateInfo, this);
24446 ds.un("add", this.updateInfo, this);
24447 this.ds = undefined;
24451 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24452 * @param {Roo.data.Store} store The data store to bind
24454 bind : function(ds){
24455 ds.on("beforeload", this.beforeLoad, this);
24456 ds.on("load", this.onLoad, this);
24457 ds.on("loadexception", this.onLoadError, this);
24458 ds.on("remove", this.updateInfo, this);
24459 ds.on("add", this.updateInfo, this);
24470 * @class Roo.bootstrap.MessageBar
24471 * @extends Roo.bootstrap.Component
24472 * Bootstrap MessageBar class
24473 * @cfg {String} html contents of the MessageBar
24474 * @cfg {String} weight (info | success | warning | danger) default info
24475 * @cfg {String} beforeClass insert the bar before the given class
24476 * @cfg {Boolean} closable (true | false) default false
24477 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24480 * Create a new Element
24481 * @param {Object} config The config object
24484 Roo.bootstrap.MessageBar = function(config){
24485 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24488 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24494 beforeClass: 'bootstrap-sticky-wrap',
24496 getAutoCreate : function(){
24500 cls: 'alert alert-dismissable alert-' + this.weight,
24505 html: this.html || ''
24511 cfg.cls += ' alert-messages-fixed';
24525 onRender : function(ct, position)
24527 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24530 var cfg = Roo.apply({}, this.getAutoCreate());
24534 cfg.cls += ' ' + this.cls;
24537 cfg.style = this.style;
24539 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24541 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24544 this.el.select('>button.close').on('click', this.hide, this);
24550 if (!this.rendered) {
24556 this.fireEvent('show', this);
24562 if (!this.rendered) {
24568 this.fireEvent('hide', this);
24571 update : function()
24573 // var e = this.el.dom.firstChild;
24575 // if(this.closable){
24576 // e = e.nextSibling;
24579 // e.data = this.html || '';
24581 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24597 * @class Roo.bootstrap.Graph
24598 * @extends Roo.bootstrap.Component
24599 * Bootstrap Graph class
24603 @cfg {String} graphtype bar | vbar | pie
24604 @cfg {number} g_x coodinator | centre x (pie)
24605 @cfg {number} g_y coodinator | centre y (pie)
24606 @cfg {number} g_r radius (pie)
24607 @cfg {number} g_height height of the chart (respected by all elements in the set)
24608 @cfg {number} g_width width of the chart (respected by all elements in the set)
24609 @cfg {Object} title The title of the chart
24612 -opts (object) options for the chart
24614 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24615 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24617 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.
24618 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24620 o stretch (boolean)
24622 -opts (object) options for the pie
24625 o startAngle (number)
24626 o endAngle (number)
24630 * Create a new Input
24631 * @param {Object} config The config object
24634 Roo.bootstrap.Graph = function(config){
24635 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24641 * The img click event for the img.
24642 * @param {Roo.EventObject} e
24648 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24659 //g_colors: this.colors,
24666 getAutoCreate : function(){
24677 onRender : function(ct,position){
24680 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24682 if (typeof(Raphael) == 'undefined') {
24683 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24687 this.raphael = Raphael(this.el.dom);
24689 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24690 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24691 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24692 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24694 r.text(160, 10, "Single Series Chart").attr(txtattr);
24695 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24696 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24697 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24699 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24700 r.barchart(330, 10, 300, 220, data1);
24701 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24702 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24705 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24706 // r.barchart(30, 30, 560, 250, xdata, {
24707 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24708 // axis : "0 0 1 1",
24709 // axisxlabels : xdata
24710 // //yvalues : cols,
24713 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24715 // this.load(null,xdata,{
24716 // axis : "0 0 1 1",
24717 // axisxlabels : xdata
24722 load : function(graphtype,xdata,opts)
24724 this.raphael.clear();
24726 graphtype = this.graphtype;
24731 var r = this.raphael,
24732 fin = function () {
24733 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24735 fout = function () {
24736 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24738 pfin = function() {
24739 this.sector.stop();
24740 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24743 this.label[0].stop();
24744 this.label[0].attr({ r: 7.5 });
24745 this.label[1].attr({ "font-weight": 800 });
24748 pfout = function() {
24749 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24752 this.label[0].animate({ r: 5 }, 500, "bounce");
24753 this.label[1].attr({ "font-weight": 400 });
24759 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24762 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24765 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
24766 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24768 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24775 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24780 setTitle: function(o)
24785 initEvents: function() {
24788 this.el.on('click', this.onClick, this);
24792 onClick : function(e)
24794 Roo.log('img onclick');
24795 this.fireEvent('click', this, e);
24807 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24810 * @class Roo.bootstrap.dash.NumberBox
24811 * @extends Roo.bootstrap.Component
24812 * Bootstrap NumberBox class
24813 * @cfg {String} headline Box headline
24814 * @cfg {String} content Box content
24815 * @cfg {String} icon Box icon
24816 * @cfg {String} footer Footer text
24817 * @cfg {String} fhref Footer href
24820 * Create a new NumberBox
24821 * @param {Object} config The config object
24825 Roo.bootstrap.dash.NumberBox = function(config){
24826 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24830 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24839 getAutoCreate : function(){
24843 cls : 'small-box ',
24851 cls : 'roo-headline',
24852 html : this.headline
24856 cls : 'roo-content',
24857 html : this.content
24871 cls : 'ion ' + this.icon
24880 cls : 'small-box-footer',
24881 href : this.fhref || '#',
24885 cfg.cn.push(footer);
24892 onRender : function(ct,position){
24893 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24900 setHeadline: function (value)
24902 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24905 setFooter: function (value, href)
24907 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24910 this.el.select('a.small-box-footer',true).first().attr('href', href);
24915 setContent: function (value)
24917 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24920 initEvents: function()
24934 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24937 * @class Roo.bootstrap.dash.TabBox
24938 * @extends Roo.bootstrap.Component
24939 * Bootstrap TabBox class
24940 * @cfg {String} title Title of the TabBox
24941 * @cfg {String} icon Icon of the TabBox
24942 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24943 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24946 * Create a new TabBox
24947 * @param {Object} config The config object
24951 Roo.bootstrap.dash.TabBox = function(config){
24952 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24957 * When a pane is added
24958 * @param {Roo.bootstrap.dash.TabPane} pane
24962 * @event activatepane
24963 * When a pane is activated
24964 * @param {Roo.bootstrap.dash.TabPane} pane
24966 "activatepane" : true
24974 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24979 tabScrollable : false,
24981 getChildContainer : function()
24983 return this.el.select('.tab-content', true).first();
24986 getAutoCreate : function(){
24990 cls: 'pull-left header',
24998 cls: 'fa ' + this.icon
25004 cls: 'nav nav-tabs pull-right',
25010 if(this.tabScrollable){
25017 cls: 'nav nav-tabs pull-right',
25028 cls: 'nav-tabs-custom',
25033 cls: 'tab-content no-padding',
25041 initEvents : function()
25043 //Roo.log('add add pane handler');
25044 this.on('addpane', this.onAddPane, this);
25047 * Updates the box title
25048 * @param {String} html to set the title to.
25050 setTitle : function(value)
25052 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25054 onAddPane : function(pane)
25056 this.panes.push(pane);
25057 //Roo.log('addpane');
25059 // tabs are rendere left to right..
25060 if(!this.showtabs){
25064 var ctr = this.el.select('.nav-tabs', true).first();
25067 var existing = ctr.select('.nav-tab',true);
25068 var qty = existing.getCount();;
25071 var tab = ctr.createChild({
25073 cls : 'nav-tab' + (qty ? '' : ' active'),
25081 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25084 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25086 pane.el.addClass('active');
25091 onTabClick : function(ev,un,ob,pane)
25093 //Roo.log('tab - prev default');
25094 ev.preventDefault();
25097 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25098 pane.tab.addClass('active');
25099 //Roo.log(pane.title);
25100 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25101 // technically we should have a deactivate event.. but maybe add later.
25102 // and it should not de-activate the selected tab...
25103 this.fireEvent('activatepane', pane);
25104 pane.el.addClass('active');
25105 pane.fireEvent('activate');
25110 getActivePane : function()
25113 Roo.each(this.panes, function(p) {
25114 if(p.el.hasClass('active')){
25135 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25137 * @class Roo.bootstrap.TabPane
25138 * @extends Roo.bootstrap.Component
25139 * Bootstrap TabPane class
25140 * @cfg {Boolean} active (false | true) Default false
25141 * @cfg {String} title title of panel
25145 * Create a new TabPane
25146 * @param {Object} config The config object
25149 Roo.bootstrap.dash.TabPane = function(config){
25150 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25156 * When a pane is activated
25157 * @param {Roo.bootstrap.dash.TabPane} pane
25164 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25169 // the tabBox that this is attached to.
25172 getAutoCreate : function()
25180 cfg.cls += ' active';
25185 initEvents : function()
25187 //Roo.log('trigger add pane handler');
25188 this.parent().fireEvent('addpane', this)
25192 * Updates the tab title
25193 * @param {String} html to set the title to.
25195 setTitle: function(str)
25201 this.tab.select('a', true).first().dom.innerHTML = str;
25218 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25221 * @class Roo.bootstrap.menu.Menu
25222 * @extends Roo.bootstrap.Component
25223 * Bootstrap Menu class - container for Menu
25224 * @cfg {String} html Text of the menu
25225 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25226 * @cfg {String} icon Font awesome icon
25227 * @cfg {String} pos Menu align to (top | bottom) default bottom
25231 * Create a new Menu
25232 * @param {Object} config The config object
25236 Roo.bootstrap.menu.Menu = function(config){
25237 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25241 * @event beforeshow
25242 * Fires before this menu is displayed
25243 * @param {Roo.bootstrap.menu.Menu} this
25247 * @event beforehide
25248 * Fires before this menu is hidden
25249 * @param {Roo.bootstrap.menu.Menu} this
25254 * Fires after this menu is displayed
25255 * @param {Roo.bootstrap.menu.Menu} this
25260 * Fires after this menu is hidden
25261 * @param {Roo.bootstrap.menu.Menu} this
25266 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25267 * @param {Roo.bootstrap.menu.Menu} this
25268 * @param {Roo.EventObject} e
25275 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25279 weight : 'default',
25284 getChildContainer : function() {
25285 if(this.isSubMenu){
25289 return this.el.select('ul.dropdown-menu', true).first();
25292 getAutoCreate : function()
25297 cls : 'roo-menu-text',
25305 cls : 'fa ' + this.icon
25316 cls : 'dropdown-button btn btn-' + this.weight,
25321 cls : 'dropdown-toggle btn btn-' + this.weight,
25331 cls : 'dropdown-menu'
25337 if(this.pos == 'top'){
25338 cfg.cls += ' dropup';
25341 if(this.isSubMenu){
25344 cls : 'dropdown-menu'
25351 onRender : function(ct, position)
25353 this.isSubMenu = ct.hasClass('dropdown-submenu');
25355 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25358 initEvents : function()
25360 if(this.isSubMenu){
25364 this.hidden = true;
25366 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25367 this.triggerEl.on('click', this.onTriggerPress, this);
25369 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25370 this.buttonEl.on('click', this.onClick, this);
25376 if(this.isSubMenu){
25380 return this.el.select('ul.dropdown-menu', true).first();
25383 onClick : function(e)
25385 this.fireEvent("click", this, e);
25388 onTriggerPress : function(e)
25390 if (this.isVisible()) {
25397 isVisible : function(){
25398 return !this.hidden;
25403 this.fireEvent("beforeshow", this);
25405 this.hidden = false;
25406 this.el.addClass('open');
25408 Roo.get(document).on("mouseup", this.onMouseUp, this);
25410 this.fireEvent("show", this);
25417 this.fireEvent("beforehide", this);
25419 this.hidden = true;
25420 this.el.removeClass('open');
25422 Roo.get(document).un("mouseup", this.onMouseUp);
25424 this.fireEvent("hide", this);
25427 onMouseUp : function()
25441 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25444 * @class Roo.bootstrap.menu.Item
25445 * @extends Roo.bootstrap.Component
25446 * Bootstrap MenuItem class
25447 * @cfg {Boolean} submenu (true | false) default false
25448 * @cfg {String} html text of the item
25449 * @cfg {String} href the link
25450 * @cfg {Boolean} disable (true | false) default false
25451 * @cfg {Boolean} preventDefault (true | false) default true
25452 * @cfg {String} icon Font awesome icon
25453 * @cfg {String} pos Submenu align to (left | right) default right
25457 * Create a new Item
25458 * @param {Object} config The config object
25462 Roo.bootstrap.menu.Item = function(config){
25463 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25467 * Fires when the mouse is hovering over this menu
25468 * @param {Roo.bootstrap.menu.Item} this
25469 * @param {Roo.EventObject} e
25474 * Fires when the mouse exits this menu
25475 * @param {Roo.bootstrap.menu.Item} this
25476 * @param {Roo.EventObject} e
25482 * The raw click event for the entire grid.
25483 * @param {Roo.EventObject} e
25489 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25494 preventDefault: true,
25499 getAutoCreate : function()
25504 cls : 'roo-menu-item-text',
25512 cls : 'fa ' + this.icon
25521 href : this.href || '#',
25528 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25532 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25534 if(this.pos == 'left'){
25535 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25542 initEvents : function()
25544 this.el.on('mouseover', this.onMouseOver, this);
25545 this.el.on('mouseout', this.onMouseOut, this);
25547 this.el.select('a', true).first().on('click', this.onClick, this);
25551 onClick : function(e)
25553 if(this.preventDefault){
25554 e.preventDefault();
25557 this.fireEvent("click", this, e);
25560 onMouseOver : function(e)
25562 if(this.submenu && this.pos == 'left'){
25563 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25566 this.fireEvent("mouseover", this, e);
25569 onMouseOut : function(e)
25571 this.fireEvent("mouseout", this, e);
25583 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25586 * @class Roo.bootstrap.menu.Separator
25587 * @extends Roo.bootstrap.Component
25588 * Bootstrap Separator class
25591 * Create a new Separator
25592 * @param {Object} config The config object
25596 Roo.bootstrap.menu.Separator = function(config){
25597 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25600 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25602 getAutoCreate : function(){
25623 * @class Roo.bootstrap.Tooltip
25624 * Bootstrap Tooltip class
25625 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25626 * to determine which dom element triggers the tooltip.
25628 * It needs to add support for additional attributes like tooltip-position
25631 * Create a new Toolti
25632 * @param {Object} config The config object
25635 Roo.bootstrap.Tooltip = function(config){
25636 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25638 this.alignment = Roo.bootstrap.Tooltip.alignment;
25640 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25641 this.alignment = config.alignment;
25646 Roo.apply(Roo.bootstrap.Tooltip, {
25648 * @function init initialize tooltip monitoring.
25652 currentTip : false,
25653 currentRegion : false,
25659 Roo.get(document).on('mouseover', this.enter ,this);
25660 Roo.get(document).on('mouseout', this.leave, this);
25663 this.currentTip = new Roo.bootstrap.Tooltip();
25666 enter : function(ev)
25668 var dom = ev.getTarget();
25670 //Roo.log(['enter',dom]);
25671 var el = Roo.fly(dom);
25672 if (this.currentEl) {
25674 //Roo.log(this.currentEl);
25675 //Roo.log(this.currentEl.contains(dom));
25676 if (this.currentEl == el) {
25679 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25685 if (this.currentTip.el) {
25686 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25690 if(!el || el.dom == document){
25696 // you can not look for children, as if el is the body.. then everythign is the child..
25697 if (!el.attr('tooltip')) { //
25698 if (!el.select("[tooltip]").elements.length) {
25701 // is the mouse over this child...?
25702 bindEl = el.select("[tooltip]").first();
25703 var xy = ev.getXY();
25704 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25705 //Roo.log("not in region.");
25708 //Roo.log("child element over..");
25711 this.currentEl = bindEl;
25712 this.currentTip.bind(bindEl);
25713 this.currentRegion = Roo.lib.Region.getRegion(dom);
25714 this.currentTip.enter();
25717 leave : function(ev)
25719 var dom = ev.getTarget();
25720 //Roo.log(['leave',dom]);
25721 if (!this.currentEl) {
25726 if (dom != this.currentEl.dom) {
25729 var xy = ev.getXY();
25730 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
25733 // only activate leave if mouse cursor is outside... bounding box..
25738 if (this.currentTip) {
25739 this.currentTip.leave();
25741 //Roo.log('clear currentEl');
25742 this.currentEl = false;
25747 'left' : ['r-l', [-2,0], 'right'],
25748 'right' : ['l-r', [2,0], 'left'],
25749 'bottom' : ['t-b', [0,2], 'top'],
25750 'top' : [ 'b-t', [0,-2], 'bottom']
25756 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
25761 delay : null, // can be { show : 300 , hide: 500}
25765 hoverState : null, //???
25767 placement : 'bottom',
25771 getAutoCreate : function(){
25778 cls : 'tooltip-arrow'
25781 cls : 'tooltip-inner'
25788 bind : function(el)
25794 enter : function () {
25796 if (this.timeout != null) {
25797 clearTimeout(this.timeout);
25800 this.hoverState = 'in';
25801 //Roo.log("enter - show");
25802 if (!this.delay || !this.delay.show) {
25807 this.timeout = setTimeout(function () {
25808 if (_t.hoverState == 'in') {
25811 }, this.delay.show);
25815 clearTimeout(this.timeout);
25817 this.hoverState = 'out';
25818 if (!this.delay || !this.delay.hide) {
25824 this.timeout = setTimeout(function () {
25825 //Roo.log("leave - timeout");
25827 if (_t.hoverState == 'out') {
25829 Roo.bootstrap.Tooltip.currentEl = false;
25834 show : function (msg)
25837 this.render(document.body);
25840 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25842 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25844 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25846 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25848 var placement = typeof this.placement == 'function' ?
25849 this.placement.call(this, this.el, on_el) :
25852 var autoToken = /\s?auto?\s?/i;
25853 var autoPlace = autoToken.test(placement);
25855 placement = placement.replace(autoToken, '') || 'top';
25859 //this.el.setXY([0,0]);
25861 //this.el.dom.style.display='block';
25863 //this.el.appendTo(on_el);
25865 var p = this.getPosition();
25866 var box = this.el.getBox();
25872 var align = this.alignment[placement];
25874 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25876 if(placement == 'top' || placement == 'bottom'){
25878 placement = 'right';
25881 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25882 placement = 'left';
25885 var scroll = Roo.select('body', true).first().getScroll();
25887 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25893 this.el.alignTo(this.bindEl, align[0],align[1]);
25894 //var arrow = this.el.select('.arrow',true).first();
25895 //arrow.set(align[2],
25897 this.el.addClass(placement);
25899 this.el.addClass('in fade');
25901 this.hoverState = null;
25903 if (this.el.hasClass('fade')) {
25914 //this.el.setXY([0,0]);
25915 this.el.removeClass('in');
25931 * @class Roo.bootstrap.LocationPicker
25932 * @extends Roo.bootstrap.Component
25933 * Bootstrap LocationPicker class
25934 * @cfg {Number} latitude Position when init default 0
25935 * @cfg {Number} longitude Position when init default 0
25936 * @cfg {Number} zoom default 15
25937 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25938 * @cfg {Boolean} mapTypeControl default false
25939 * @cfg {Boolean} disableDoubleClickZoom default false
25940 * @cfg {Boolean} scrollwheel default true
25941 * @cfg {Boolean} streetViewControl default false
25942 * @cfg {Number} radius default 0
25943 * @cfg {String} locationName
25944 * @cfg {Boolean} draggable default true
25945 * @cfg {Boolean} enableAutocomplete default false
25946 * @cfg {Boolean} enableReverseGeocode default true
25947 * @cfg {String} markerTitle
25950 * Create a new LocationPicker
25951 * @param {Object} config The config object
25955 Roo.bootstrap.LocationPicker = function(config){
25957 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25962 * Fires when the picker initialized.
25963 * @param {Roo.bootstrap.LocationPicker} this
25964 * @param {Google Location} location
25968 * @event positionchanged
25969 * Fires when the picker position changed.
25970 * @param {Roo.bootstrap.LocationPicker} this
25971 * @param {Google Location} location
25973 positionchanged : true,
25976 * Fires when the map resize.
25977 * @param {Roo.bootstrap.LocationPicker} this
25982 * Fires when the map show.
25983 * @param {Roo.bootstrap.LocationPicker} this
25988 * Fires when the map hide.
25989 * @param {Roo.bootstrap.LocationPicker} this
25994 * Fires when click the map.
25995 * @param {Roo.bootstrap.LocationPicker} this
25996 * @param {Map event} e
26000 * @event mapRightClick
26001 * Fires when right click the map.
26002 * @param {Roo.bootstrap.LocationPicker} this
26003 * @param {Map event} e
26005 mapRightClick : true,
26007 * @event markerClick
26008 * Fires when click the marker.
26009 * @param {Roo.bootstrap.LocationPicker} this
26010 * @param {Map event} e
26012 markerClick : true,
26014 * @event markerRightClick
26015 * Fires when right click the marker.
26016 * @param {Roo.bootstrap.LocationPicker} this
26017 * @param {Map event} e
26019 markerRightClick : true,
26021 * @event OverlayViewDraw
26022 * Fires when OverlayView Draw
26023 * @param {Roo.bootstrap.LocationPicker} this
26025 OverlayViewDraw : true,
26027 * @event OverlayViewOnAdd
26028 * Fires when OverlayView Draw
26029 * @param {Roo.bootstrap.LocationPicker} this
26031 OverlayViewOnAdd : true,
26033 * @event OverlayViewOnRemove
26034 * Fires when OverlayView Draw
26035 * @param {Roo.bootstrap.LocationPicker} this
26037 OverlayViewOnRemove : true,
26039 * @event OverlayViewShow
26040 * Fires when OverlayView Draw
26041 * @param {Roo.bootstrap.LocationPicker} this
26042 * @param {Pixel} cpx
26044 OverlayViewShow : true,
26046 * @event OverlayViewHide
26047 * Fires when OverlayView Draw
26048 * @param {Roo.bootstrap.LocationPicker} this
26050 OverlayViewHide : true,
26052 * @event loadexception
26053 * Fires when load google lib failed.
26054 * @param {Roo.bootstrap.LocationPicker} this
26056 loadexception : true
26061 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26063 gMapContext: false,
26069 mapTypeControl: false,
26070 disableDoubleClickZoom: false,
26072 streetViewControl: false,
26076 enableAutocomplete: false,
26077 enableReverseGeocode: true,
26080 getAutoCreate: function()
26085 cls: 'roo-location-picker'
26091 initEvents: function(ct, position)
26093 if(!this.el.getWidth() || this.isApplied()){
26097 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26102 initial: function()
26104 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26105 this.fireEvent('loadexception', this);
26109 if(!this.mapTypeId){
26110 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26113 this.gMapContext = this.GMapContext();
26115 this.initOverlayView();
26117 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26121 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26122 _this.setPosition(_this.gMapContext.marker.position);
26125 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26126 _this.fireEvent('mapClick', this, event);
26130 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26131 _this.fireEvent('mapRightClick', this, event);
26135 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26136 _this.fireEvent('markerClick', this, event);
26140 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26141 _this.fireEvent('markerRightClick', this, event);
26145 this.setPosition(this.gMapContext.location);
26147 this.fireEvent('initial', this, this.gMapContext.location);
26150 initOverlayView: function()
26154 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26158 _this.fireEvent('OverlayViewDraw', _this);
26163 _this.fireEvent('OverlayViewOnAdd', _this);
26166 onRemove: function()
26168 _this.fireEvent('OverlayViewOnRemove', _this);
26171 show: function(cpx)
26173 _this.fireEvent('OverlayViewShow', _this, cpx);
26178 _this.fireEvent('OverlayViewHide', _this);
26184 fromLatLngToContainerPixel: function(event)
26186 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26189 isApplied: function()
26191 return this.getGmapContext() == false ? false : true;
26194 getGmapContext: function()
26196 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26199 GMapContext: function()
26201 var position = new google.maps.LatLng(this.latitude, this.longitude);
26203 var _map = new google.maps.Map(this.el.dom, {
26206 mapTypeId: this.mapTypeId,
26207 mapTypeControl: this.mapTypeControl,
26208 disableDoubleClickZoom: this.disableDoubleClickZoom,
26209 scrollwheel: this.scrollwheel,
26210 streetViewControl: this.streetViewControl,
26211 locationName: this.locationName,
26212 draggable: this.draggable,
26213 enableAutocomplete: this.enableAutocomplete,
26214 enableReverseGeocode: this.enableReverseGeocode
26217 var _marker = new google.maps.Marker({
26218 position: position,
26220 title: this.markerTitle,
26221 draggable: this.draggable
26228 location: position,
26229 radius: this.radius,
26230 locationName: this.locationName,
26231 addressComponents: {
26232 formatted_address: null,
26233 addressLine1: null,
26234 addressLine2: null,
26236 streetNumber: null,
26240 stateOrProvince: null
26243 domContainer: this.el.dom,
26244 geodecoder: new google.maps.Geocoder()
26248 drawCircle: function(center, radius, options)
26250 if (this.gMapContext.circle != null) {
26251 this.gMapContext.circle.setMap(null);
26255 options = Roo.apply({}, options, {
26256 strokeColor: "#0000FF",
26257 strokeOpacity: .35,
26259 fillColor: "#0000FF",
26263 options.map = this.gMapContext.map;
26264 options.radius = radius;
26265 options.center = center;
26266 this.gMapContext.circle = new google.maps.Circle(options);
26267 return this.gMapContext.circle;
26273 setPosition: function(location)
26275 this.gMapContext.location = location;
26276 this.gMapContext.marker.setPosition(location);
26277 this.gMapContext.map.panTo(location);
26278 this.drawCircle(location, this.gMapContext.radius, {});
26282 if (this.gMapContext.settings.enableReverseGeocode) {
26283 this.gMapContext.geodecoder.geocode({
26284 latLng: this.gMapContext.location
26285 }, function(results, status) {
26287 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26288 _this.gMapContext.locationName = results[0].formatted_address;
26289 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26291 _this.fireEvent('positionchanged', this, location);
26298 this.fireEvent('positionchanged', this, location);
26303 google.maps.event.trigger(this.gMapContext.map, "resize");
26305 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26307 this.fireEvent('resize', this);
26310 setPositionByLatLng: function(latitude, longitude)
26312 this.setPosition(new google.maps.LatLng(latitude, longitude));
26315 getCurrentPosition: function()
26318 latitude: this.gMapContext.location.lat(),
26319 longitude: this.gMapContext.location.lng()
26323 getAddressName: function()
26325 return this.gMapContext.locationName;
26328 getAddressComponents: function()
26330 return this.gMapContext.addressComponents;
26333 address_component_from_google_geocode: function(address_components)
26337 for (var i = 0; i < address_components.length; i++) {
26338 var component = address_components[i];
26339 if (component.types.indexOf("postal_code") >= 0) {
26340 result.postalCode = component.short_name;
26341 } else if (component.types.indexOf("street_number") >= 0) {
26342 result.streetNumber = component.short_name;
26343 } else if (component.types.indexOf("route") >= 0) {
26344 result.streetName = component.short_name;
26345 } else if (component.types.indexOf("neighborhood") >= 0) {
26346 result.city = component.short_name;
26347 } else if (component.types.indexOf("locality") >= 0) {
26348 result.city = component.short_name;
26349 } else if (component.types.indexOf("sublocality") >= 0) {
26350 result.district = component.short_name;
26351 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26352 result.stateOrProvince = component.short_name;
26353 } else if (component.types.indexOf("country") >= 0) {
26354 result.country = component.short_name;
26358 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26359 result.addressLine2 = "";
26363 setZoomLevel: function(zoom)
26365 this.gMapContext.map.setZoom(zoom);
26378 this.fireEvent('show', this);
26389 this.fireEvent('hide', this);
26394 Roo.apply(Roo.bootstrap.LocationPicker, {
26396 OverlayView : function(map, options)
26398 options = options || {};
26412 * @class Roo.bootstrap.Alert
26413 * @extends Roo.bootstrap.Component
26414 * Bootstrap Alert class
26415 * @cfg {String} title The title of alert
26416 * @cfg {String} html The content of alert
26417 * @cfg {String} weight ( success | info | warning | danger )
26418 * @cfg {String} faicon font-awesomeicon
26421 * Create a new alert
26422 * @param {Object} config The config object
26426 Roo.bootstrap.Alert = function(config){
26427 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26431 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26438 getAutoCreate : function()
26447 cls : 'roo-alert-icon'
26452 cls : 'roo-alert-title',
26457 cls : 'roo-alert-text',
26464 cfg.cn[0].cls += ' fa ' + this.faicon;
26468 cfg.cls += ' alert-' + this.weight;
26474 initEvents: function()
26476 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26479 setTitle : function(str)
26481 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26484 setText : function(str)
26486 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26489 setWeight : function(weight)
26492 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26495 this.weight = weight;
26497 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26500 setIcon : function(icon)
26503 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26506 this.faicon = icon;
26508 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26529 * @class Roo.bootstrap.UploadCropbox
26530 * @extends Roo.bootstrap.Component
26531 * Bootstrap UploadCropbox class
26532 * @cfg {String} emptyText show when image has been loaded
26533 * @cfg {String} rotateNotify show when image too small to rotate
26534 * @cfg {Number} errorTimeout default 3000
26535 * @cfg {Number} minWidth default 300
26536 * @cfg {Number} minHeight default 300
26537 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26538 * @cfg {Boolean} isDocument (true|false) default false
26539 * @cfg {String} url action url
26540 * @cfg {String} paramName default 'imageUpload'
26541 * @cfg {String} method default POST
26542 * @cfg {Boolean} loadMask (true|false) default true
26543 * @cfg {Boolean} loadingText default 'Loading...'
26546 * Create a new UploadCropbox
26547 * @param {Object} config The config object
26550 Roo.bootstrap.UploadCropbox = function(config){
26551 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26555 * @event beforeselectfile
26556 * Fire before select file
26557 * @param {Roo.bootstrap.UploadCropbox} this
26559 "beforeselectfile" : true,
26562 * Fire after initEvent
26563 * @param {Roo.bootstrap.UploadCropbox} this
26568 * Fire after initEvent
26569 * @param {Roo.bootstrap.UploadCropbox} this
26570 * @param {String} data
26575 * Fire when preparing the file data
26576 * @param {Roo.bootstrap.UploadCropbox} this
26577 * @param {Object} file
26582 * Fire when get exception
26583 * @param {Roo.bootstrap.UploadCropbox} this
26584 * @param {XMLHttpRequest} xhr
26586 "exception" : true,
26588 * @event beforeloadcanvas
26589 * Fire before load the canvas
26590 * @param {Roo.bootstrap.UploadCropbox} this
26591 * @param {String} src
26593 "beforeloadcanvas" : true,
26596 * Fire when trash image
26597 * @param {Roo.bootstrap.UploadCropbox} this
26602 * Fire when download the image
26603 * @param {Roo.bootstrap.UploadCropbox} this
26607 * @event footerbuttonclick
26608 * Fire when footerbuttonclick
26609 * @param {Roo.bootstrap.UploadCropbox} this
26610 * @param {String} type
26612 "footerbuttonclick" : true,
26616 * @param {Roo.bootstrap.UploadCropbox} this
26621 * Fire when rotate the image
26622 * @param {Roo.bootstrap.UploadCropbox} this
26623 * @param {String} pos
26628 * Fire when inspect the file
26629 * @param {Roo.bootstrap.UploadCropbox} this
26630 * @param {Object} file
26635 * Fire when xhr upload the file
26636 * @param {Roo.bootstrap.UploadCropbox} this
26637 * @param {Object} data
26642 * Fire when arrange the file data
26643 * @param {Roo.bootstrap.UploadCropbox} this
26644 * @param {Object} formData
26649 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26652 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26654 emptyText : 'Click to upload image',
26655 rotateNotify : 'Image is too small to rotate',
26656 errorTimeout : 3000,
26670 cropType : 'image/jpeg',
26672 canvasLoaded : false,
26673 isDocument : false,
26675 paramName : 'imageUpload',
26677 loadingText : 'Loading...',
26680 getAutoCreate : function()
26684 cls : 'roo-upload-cropbox',
26688 cls : 'roo-upload-cropbox-selector',
26693 cls : 'roo-upload-cropbox-body',
26694 style : 'cursor:pointer',
26698 cls : 'roo-upload-cropbox-preview'
26702 cls : 'roo-upload-cropbox-thumb'
26706 cls : 'roo-upload-cropbox-empty-notify',
26707 html : this.emptyText
26711 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26712 html : this.rotateNotify
26718 cls : 'roo-upload-cropbox-footer',
26721 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26731 onRender : function(ct, position)
26733 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26735 if (this.buttons.length) {
26737 Roo.each(this.buttons, function(bb) {
26739 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26741 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26747 this.maskEl = this.el;
26751 initEvents : function()
26753 this.urlAPI = (window.createObjectURL && window) ||
26754 (window.URL && URL.revokeObjectURL && URL) ||
26755 (window.webkitURL && webkitURL);
26757 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26758 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26760 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26761 this.selectorEl.hide();
26763 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26764 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26766 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26767 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26768 this.thumbEl.hide();
26770 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26771 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26773 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26774 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26775 this.errorEl.hide();
26777 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26778 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26779 this.footerEl.hide();
26781 this.setThumbBoxSize();
26787 this.fireEvent('initial', this);
26794 window.addEventListener("resize", function() { _this.resize(); } );
26796 this.bodyEl.on('click', this.beforeSelectFile, this);
26799 this.bodyEl.on('touchstart', this.onTouchStart, this);
26800 this.bodyEl.on('touchmove', this.onTouchMove, this);
26801 this.bodyEl.on('touchend', this.onTouchEnd, this);
26805 this.bodyEl.on('mousedown', this.onMouseDown, this);
26806 this.bodyEl.on('mousemove', this.onMouseMove, this);
26807 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26808 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26809 Roo.get(document).on('mouseup', this.onMouseUp, this);
26812 this.selectorEl.on('change', this.onFileSelected, this);
26818 this.baseScale = 1;
26820 this.baseRotate = 1;
26821 this.dragable = false;
26822 this.pinching = false;
26825 this.cropData = false;
26826 this.notifyEl.dom.innerHTML = this.emptyText;
26828 this.selectorEl.dom.value = '';
26832 resize : function()
26834 if(this.fireEvent('resize', this) != false){
26835 this.setThumbBoxPosition();
26836 this.setCanvasPosition();
26840 onFooterButtonClick : function(e, el, o, type)
26843 case 'rotate-left' :
26844 this.onRotateLeft(e);
26846 case 'rotate-right' :
26847 this.onRotateRight(e);
26850 this.beforeSelectFile(e);
26865 this.fireEvent('footerbuttonclick', this, type);
26868 beforeSelectFile : function(e)
26870 e.preventDefault();
26872 if(this.fireEvent('beforeselectfile', this) != false){
26873 this.selectorEl.dom.click();
26877 onFileSelected : function(e)
26879 e.preventDefault();
26881 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26885 var file = this.selectorEl.dom.files[0];
26887 if(this.fireEvent('inspect', this, file) != false){
26888 this.prepare(file);
26893 trash : function(e)
26895 this.fireEvent('trash', this);
26898 download : function(e)
26900 this.fireEvent('download', this);
26903 loadCanvas : function(src)
26905 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26909 this.imageEl = document.createElement('img');
26913 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26915 this.imageEl.src = src;
26919 onLoadCanvas : function()
26921 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26922 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26924 this.bodyEl.un('click', this.beforeSelectFile, this);
26926 this.notifyEl.hide();
26927 this.thumbEl.show();
26928 this.footerEl.show();
26930 this.baseRotateLevel();
26932 if(this.isDocument){
26933 this.setThumbBoxSize();
26936 this.setThumbBoxPosition();
26938 this.baseScaleLevel();
26944 this.canvasLoaded = true;
26947 this.maskEl.unmask();
26952 setCanvasPosition : function()
26954 if(!this.canvasEl){
26958 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26959 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26961 this.previewEl.setLeft(pw);
26962 this.previewEl.setTop(ph);
26966 onMouseDown : function(e)
26970 this.dragable = true;
26971 this.pinching = false;
26973 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26974 this.dragable = false;
26978 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26979 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26983 onMouseMove : function(e)
26987 if(!this.canvasLoaded){
26991 if (!this.dragable){
26995 var minX = Math.ceil(this.thumbEl.getLeft(true));
26996 var minY = Math.ceil(this.thumbEl.getTop(true));
26998 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26999 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27001 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27002 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27004 x = x - this.mouseX;
27005 y = y - this.mouseY;
27007 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27008 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27010 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27011 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27013 this.previewEl.setLeft(bgX);
27014 this.previewEl.setTop(bgY);
27016 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27017 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27020 onMouseUp : function(e)
27024 this.dragable = false;
27027 onMouseWheel : function(e)
27031 this.startScale = this.scale;
27033 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27035 if(!this.zoomable()){
27036 this.scale = this.startScale;
27045 zoomable : function()
27047 var minScale = this.thumbEl.getWidth() / this.minWidth;
27049 if(this.minWidth < this.minHeight){
27050 minScale = this.thumbEl.getHeight() / this.minHeight;
27053 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27054 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27058 (this.rotate == 0 || this.rotate == 180) &&
27060 width > this.imageEl.OriginWidth ||
27061 height > this.imageEl.OriginHeight ||
27062 (width < this.minWidth && height < this.minHeight)
27070 (this.rotate == 90 || this.rotate == 270) &&
27072 width > this.imageEl.OriginWidth ||
27073 height > this.imageEl.OriginHeight ||
27074 (width < this.minHeight && height < this.minWidth)
27081 !this.isDocument &&
27082 (this.rotate == 0 || this.rotate == 180) &&
27084 width < this.minWidth ||
27085 width > this.imageEl.OriginWidth ||
27086 height < this.minHeight ||
27087 height > this.imageEl.OriginHeight
27094 !this.isDocument &&
27095 (this.rotate == 90 || this.rotate == 270) &&
27097 width < this.minHeight ||
27098 width > this.imageEl.OriginWidth ||
27099 height < this.minWidth ||
27100 height > this.imageEl.OriginHeight
27110 onRotateLeft : function(e)
27112 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27114 var minScale = this.thumbEl.getWidth() / this.minWidth;
27116 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27117 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27119 this.startScale = this.scale;
27121 while (this.getScaleLevel() < minScale){
27123 this.scale = this.scale + 1;
27125 if(!this.zoomable()){
27130 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27131 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27136 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27143 this.scale = this.startScale;
27145 this.onRotateFail();
27150 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27152 if(this.isDocument){
27153 this.setThumbBoxSize();
27154 this.setThumbBoxPosition();
27155 this.setCanvasPosition();
27160 this.fireEvent('rotate', this, 'left');
27164 onRotateRight : function(e)
27166 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27168 var minScale = this.thumbEl.getWidth() / this.minWidth;
27170 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27171 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27173 this.startScale = this.scale;
27175 while (this.getScaleLevel() < minScale){
27177 this.scale = this.scale + 1;
27179 if(!this.zoomable()){
27184 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27185 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27190 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27197 this.scale = this.startScale;
27199 this.onRotateFail();
27204 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27206 if(this.isDocument){
27207 this.setThumbBoxSize();
27208 this.setThumbBoxPosition();
27209 this.setCanvasPosition();
27214 this.fireEvent('rotate', this, 'right');
27217 onRotateFail : function()
27219 this.errorEl.show(true);
27223 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27228 this.previewEl.dom.innerHTML = '';
27230 var canvasEl = document.createElement("canvas");
27232 var contextEl = canvasEl.getContext("2d");
27234 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27235 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27236 var center = this.imageEl.OriginWidth / 2;
27238 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27239 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27240 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27241 center = this.imageEl.OriginHeight / 2;
27244 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27246 contextEl.translate(center, center);
27247 contextEl.rotate(this.rotate * Math.PI / 180);
27249 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27251 this.canvasEl = document.createElement("canvas");
27253 this.contextEl = this.canvasEl.getContext("2d");
27255 switch (this.rotate) {
27258 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27259 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27261 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27266 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27267 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27269 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27270 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);
27274 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27279 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27280 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27282 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27283 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);
27287 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);
27292 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27293 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27295 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27296 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27300 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);
27307 this.previewEl.appendChild(this.canvasEl);
27309 this.setCanvasPosition();
27314 if(!this.canvasLoaded){
27318 var imageCanvas = document.createElement("canvas");
27320 var imageContext = imageCanvas.getContext("2d");
27322 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27323 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27325 var center = imageCanvas.width / 2;
27327 imageContext.translate(center, center);
27329 imageContext.rotate(this.rotate * Math.PI / 180);
27331 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27333 var canvas = document.createElement("canvas");
27335 var context = canvas.getContext("2d");
27337 canvas.width = this.minWidth;
27338 canvas.height = this.minHeight;
27340 switch (this.rotate) {
27343 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27344 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27346 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27347 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27349 var targetWidth = this.minWidth - 2 * x;
27350 var targetHeight = this.minHeight - 2 * y;
27354 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27355 scale = targetWidth / width;
27358 if(x > 0 && y == 0){
27359 scale = targetHeight / height;
27362 if(x > 0 && y > 0){
27363 scale = targetWidth / width;
27365 if(width < height){
27366 scale = targetHeight / height;
27370 context.scale(scale, scale);
27372 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27373 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27375 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27376 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27378 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27383 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27384 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27386 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27387 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27389 var targetWidth = this.minWidth - 2 * x;
27390 var targetHeight = this.minHeight - 2 * y;
27394 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27395 scale = targetWidth / width;
27398 if(x > 0 && y == 0){
27399 scale = targetHeight / height;
27402 if(x > 0 && y > 0){
27403 scale = targetWidth / width;
27405 if(width < height){
27406 scale = targetHeight / height;
27410 context.scale(scale, scale);
27412 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27413 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27415 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27416 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27418 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27420 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27425 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27426 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27428 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27429 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27431 var targetWidth = this.minWidth - 2 * x;
27432 var targetHeight = this.minHeight - 2 * y;
27436 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27437 scale = targetWidth / width;
27440 if(x > 0 && y == 0){
27441 scale = targetHeight / height;
27444 if(x > 0 && y > 0){
27445 scale = targetWidth / width;
27447 if(width < height){
27448 scale = targetHeight / height;
27452 context.scale(scale, scale);
27454 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27455 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27457 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27458 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27460 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27461 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27463 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27468 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27469 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27471 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27472 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27474 var targetWidth = this.minWidth - 2 * x;
27475 var targetHeight = this.minHeight - 2 * y;
27479 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27480 scale = targetWidth / width;
27483 if(x > 0 && y == 0){
27484 scale = targetHeight / height;
27487 if(x > 0 && y > 0){
27488 scale = targetWidth / width;
27490 if(width < height){
27491 scale = targetHeight / height;
27495 context.scale(scale, scale);
27497 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27498 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27500 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27501 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27503 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27505 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27512 this.cropData = canvas.toDataURL(this.cropType);
27514 if(this.fireEvent('crop', this, this.cropData) !== false){
27515 this.process(this.file, this.cropData);
27522 setThumbBoxSize : function()
27526 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27527 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27528 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27530 this.minWidth = width;
27531 this.minHeight = height;
27533 if(this.rotate == 90 || this.rotate == 270){
27534 this.minWidth = height;
27535 this.minHeight = width;
27540 width = Math.ceil(this.minWidth * height / this.minHeight);
27542 if(this.minWidth > this.minHeight){
27544 height = Math.ceil(this.minHeight * width / this.minWidth);
27547 this.thumbEl.setStyle({
27548 width : width + 'px',
27549 height : height + 'px'
27556 setThumbBoxPosition : function()
27558 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27559 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27561 this.thumbEl.setLeft(x);
27562 this.thumbEl.setTop(y);
27566 baseRotateLevel : function()
27568 this.baseRotate = 1;
27571 typeof(this.exif) != 'undefined' &&
27572 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27573 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27575 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27578 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27582 baseScaleLevel : function()
27586 if(this.isDocument){
27588 if(this.baseRotate == 6 || this.baseRotate == 8){
27590 height = this.thumbEl.getHeight();
27591 this.baseScale = height / this.imageEl.OriginWidth;
27593 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27594 width = this.thumbEl.getWidth();
27595 this.baseScale = width / this.imageEl.OriginHeight;
27601 height = this.thumbEl.getHeight();
27602 this.baseScale = height / this.imageEl.OriginHeight;
27604 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27605 width = this.thumbEl.getWidth();
27606 this.baseScale = width / this.imageEl.OriginWidth;
27612 if(this.baseRotate == 6 || this.baseRotate == 8){
27614 width = this.thumbEl.getHeight();
27615 this.baseScale = width / this.imageEl.OriginHeight;
27617 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27618 height = this.thumbEl.getWidth();
27619 this.baseScale = height / this.imageEl.OriginHeight;
27622 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27623 height = this.thumbEl.getWidth();
27624 this.baseScale = height / this.imageEl.OriginHeight;
27626 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27627 width = this.thumbEl.getHeight();
27628 this.baseScale = width / this.imageEl.OriginWidth;
27635 width = this.thumbEl.getWidth();
27636 this.baseScale = width / this.imageEl.OriginWidth;
27638 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27639 height = this.thumbEl.getHeight();
27640 this.baseScale = height / this.imageEl.OriginHeight;
27643 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27645 height = this.thumbEl.getHeight();
27646 this.baseScale = height / this.imageEl.OriginHeight;
27648 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27649 width = this.thumbEl.getWidth();
27650 this.baseScale = width / this.imageEl.OriginWidth;
27658 getScaleLevel : function()
27660 return this.baseScale * Math.pow(1.1, this.scale);
27663 onTouchStart : function(e)
27665 if(!this.canvasLoaded){
27666 this.beforeSelectFile(e);
27670 var touches = e.browserEvent.touches;
27676 if(touches.length == 1){
27677 this.onMouseDown(e);
27681 if(touches.length != 2){
27687 for(var i = 0, finger; finger = touches[i]; i++){
27688 coords.push(finger.pageX, finger.pageY);
27691 var x = Math.pow(coords[0] - coords[2], 2);
27692 var y = Math.pow(coords[1] - coords[3], 2);
27694 this.startDistance = Math.sqrt(x + y);
27696 this.startScale = this.scale;
27698 this.pinching = true;
27699 this.dragable = false;
27703 onTouchMove : function(e)
27705 if(!this.pinching && !this.dragable){
27709 var touches = e.browserEvent.touches;
27716 this.onMouseMove(e);
27722 for(var i = 0, finger; finger = touches[i]; i++){
27723 coords.push(finger.pageX, finger.pageY);
27726 var x = Math.pow(coords[0] - coords[2], 2);
27727 var y = Math.pow(coords[1] - coords[3], 2);
27729 this.endDistance = Math.sqrt(x + y);
27731 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27733 if(!this.zoomable()){
27734 this.scale = this.startScale;
27742 onTouchEnd : function(e)
27744 this.pinching = false;
27745 this.dragable = false;
27749 process : function(file, crop)
27752 this.maskEl.mask(this.loadingText);
27755 this.xhr = new XMLHttpRequest();
27757 file.xhr = this.xhr;
27759 this.xhr.open(this.method, this.url, true);
27762 "Accept": "application/json",
27763 "Cache-Control": "no-cache",
27764 "X-Requested-With": "XMLHttpRequest"
27767 for (var headerName in headers) {
27768 var headerValue = headers[headerName];
27770 this.xhr.setRequestHeader(headerName, headerValue);
27776 this.xhr.onload = function()
27778 _this.xhrOnLoad(_this.xhr);
27781 this.xhr.onerror = function()
27783 _this.xhrOnError(_this.xhr);
27786 var formData = new FormData();
27788 formData.append('returnHTML', 'NO');
27791 formData.append('crop', crop);
27794 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27795 formData.append(this.paramName, file, file.name);
27798 if(typeof(file.filename) != 'undefined'){
27799 formData.append('filename', file.filename);
27802 if(typeof(file.mimetype) != 'undefined'){
27803 formData.append('mimetype', file.mimetype);
27806 if(this.fireEvent('arrange', this, formData) != false){
27807 this.xhr.send(formData);
27811 xhrOnLoad : function(xhr)
27814 this.maskEl.unmask();
27817 if (xhr.readyState !== 4) {
27818 this.fireEvent('exception', this, xhr);
27822 var response = Roo.decode(xhr.responseText);
27824 if(!response.success){
27825 this.fireEvent('exception', this, xhr);
27829 var response = Roo.decode(xhr.responseText);
27831 this.fireEvent('upload', this, response);
27835 xhrOnError : function()
27838 this.maskEl.unmask();
27841 Roo.log('xhr on error');
27843 var response = Roo.decode(xhr.responseText);
27849 prepare : function(file)
27852 this.maskEl.mask(this.loadingText);
27858 if(typeof(file) === 'string'){
27859 this.loadCanvas(file);
27863 if(!file || !this.urlAPI){
27868 this.cropType = file.type;
27872 if(this.fireEvent('prepare', this, this.file) != false){
27874 var reader = new FileReader();
27876 reader.onload = function (e) {
27877 if (e.target.error) {
27878 Roo.log(e.target.error);
27882 var buffer = e.target.result,
27883 dataView = new DataView(buffer),
27885 maxOffset = dataView.byteLength - 4,
27889 if (dataView.getUint16(0) === 0xffd8) {
27890 while (offset < maxOffset) {
27891 markerBytes = dataView.getUint16(offset);
27893 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27894 markerLength = dataView.getUint16(offset + 2) + 2;
27895 if (offset + markerLength > dataView.byteLength) {
27896 Roo.log('Invalid meta data: Invalid segment size.');
27900 if(markerBytes == 0xffe1){
27901 _this.parseExifData(
27908 offset += markerLength;
27918 var url = _this.urlAPI.createObjectURL(_this.file);
27920 _this.loadCanvas(url);
27925 reader.readAsArrayBuffer(this.file);
27931 parseExifData : function(dataView, offset, length)
27933 var tiffOffset = offset + 10,
27937 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27938 // No Exif data, might be XMP data instead
27942 // Check for the ASCII code for "Exif" (0x45786966):
27943 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27944 // No Exif data, might be XMP data instead
27947 if (tiffOffset + 8 > dataView.byteLength) {
27948 Roo.log('Invalid Exif data: Invalid segment size.');
27951 // Check for the two null bytes:
27952 if (dataView.getUint16(offset + 8) !== 0x0000) {
27953 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27956 // Check the byte alignment:
27957 switch (dataView.getUint16(tiffOffset)) {
27959 littleEndian = true;
27962 littleEndian = false;
27965 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27968 // Check for the TIFF tag marker (0x002A):
27969 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27970 Roo.log('Invalid Exif data: Missing TIFF marker.');
27973 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27974 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27976 this.parseExifTags(
27979 tiffOffset + dirOffset,
27984 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27989 if (dirOffset + 6 > dataView.byteLength) {
27990 Roo.log('Invalid Exif data: Invalid directory offset.');
27993 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27994 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27995 if (dirEndOffset + 4 > dataView.byteLength) {
27996 Roo.log('Invalid Exif data: Invalid directory size.');
27999 for (i = 0; i < tagsNumber; i += 1) {
28003 dirOffset + 2 + 12 * i, // tag offset
28007 // Return the offset to the next directory:
28008 return dataView.getUint32(dirEndOffset, littleEndian);
28011 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28013 var tag = dataView.getUint16(offset, littleEndian);
28015 this.exif[tag] = this.getExifValue(
28019 dataView.getUint16(offset + 2, littleEndian), // tag type
28020 dataView.getUint32(offset + 4, littleEndian), // tag length
28025 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28027 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28036 Roo.log('Invalid Exif data: Invalid tag type.');
28040 tagSize = tagType.size * length;
28041 // Determine if the value is contained in the dataOffset bytes,
28042 // or if the value at the dataOffset is a pointer to the actual data:
28043 dataOffset = tagSize > 4 ?
28044 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28045 if (dataOffset + tagSize > dataView.byteLength) {
28046 Roo.log('Invalid Exif data: Invalid data offset.');
28049 if (length === 1) {
28050 return tagType.getValue(dataView, dataOffset, littleEndian);
28053 for (i = 0; i < length; i += 1) {
28054 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28057 if (tagType.ascii) {
28059 // Concatenate the chars:
28060 for (i = 0; i < values.length; i += 1) {
28062 // Ignore the terminating NULL byte(s):
28063 if (c === '\u0000') {
28075 Roo.apply(Roo.bootstrap.UploadCropbox, {
28077 'Orientation': 0x0112
28081 1: 0, //'top-left',
28083 3: 180, //'bottom-right',
28084 // 4: 'bottom-left',
28086 6: 90, //'right-top',
28087 // 7: 'right-bottom',
28088 8: 270 //'left-bottom'
28092 // byte, 8-bit unsigned int:
28094 getValue: function (dataView, dataOffset) {
28095 return dataView.getUint8(dataOffset);
28099 // ascii, 8-bit byte:
28101 getValue: function (dataView, dataOffset) {
28102 return String.fromCharCode(dataView.getUint8(dataOffset));
28107 // short, 16 bit int:
28109 getValue: function (dataView, dataOffset, littleEndian) {
28110 return dataView.getUint16(dataOffset, littleEndian);
28114 // long, 32 bit int:
28116 getValue: function (dataView, dataOffset, littleEndian) {
28117 return dataView.getUint32(dataOffset, littleEndian);
28121 // rational = two long values, first is numerator, second is denominator:
28123 getValue: function (dataView, dataOffset, littleEndian) {
28124 return dataView.getUint32(dataOffset, littleEndian) /
28125 dataView.getUint32(dataOffset + 4, littleEndian);
28129 // slong, 32 bit signed int:
28131 getValue: function (dataView, dataOffset, littleEndian) {
28132 return dataView.getInt32(dataOffset, littleEndian);
28136 // srational, two slongs, first is numerator, second is denominator:
28138 getValue: function (dataView, dataOffset, littleEndian) {
28139 return dataView.getInt32(dataOffset, littleEndian) /
28140 dataView.getInt32(dataOffset + 4, littleEndian);
28150 cls : 'btn-group roo-upload-cropbox-rotate-left',
28151 action : 'rotate-left',
28155 cls : 'btn btn-default',
28156 html : '<i class="fa fa-undo"></i>'
28162 cls : 'btn-group roo-upload-cropbox-picture',
28163 action : 'picture',
28167 cls : 'btn btn-default',
28168 html : '<i class="fa fa-picture-o"></i>'
28174 cls : 'btn-group roo-upload-cropbox-rotate-right',
28175 action : 'rotate-right',
28179 cls : 'btn btn-default',
28180 html : '<i class="fa fa-repeat"></i>'
28188 cls : 'btn-group roo-upload-cropbox-rotate-left',
28189 action : 'rotate-left',
28193 cls : 'btn btn-default',
28194 html : '<i class="fa fa-undo"></i>'
28200 cls : 'btn-group roo-upload-cropbox-download',
28201 action : 'download',
28205 cls : 'btn btn-default',
28206 html : '<i class="fa fa-download"></i>'
28212 cls : 'btn-group roo-upload-cropbox-crop',
28217 cls : 'btn btn-default',
28218 html : '<i class="fa fa-crop"></i>'
28224 cls : 'btn-group roo-upload-cropbox-trash',
28229 cls : 'btn btn-default',
28230 html : '<i class="fa fa-trash"></i>'
28236 cls : 'btn-group roo-upload-cropbox-rotate-right',
28237 action : 'rotate-right',
28241 cls : 'btn btn-default',
28242 html : '<i class="fa fa-repeat"></i>'
28250 cls : 'btn-group roo-upload-cropbox-rotate-left',
28251 action : 'rotate-left',
28255 cls : 'btn btn-default',
28256 html : '<i class="fa fa-undo"></i>'
28262 cls : 'btn-group roo-upload-cropbox-rotate-right',
28263 action : 'rotate-right',
28267 cls : 'btn btn-default',
28268 html : '<i class="fa fa-repeat"></i>'
28281 * @class Roo.bootstrap.DocumentManager
28282 * @extends Roo.bootstrap.Component
28283 * Bootstrap DocumentManager class
28284 * @cfg {String} paramName default 'imageUpload'
28285 * @cfg {String} toolTipName default 'filename'
28286 * @cfg {String} method default POST
28287 * @cfg {String} url action url
28288 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28289 * @cfg {Boolean} multiple multiple upload default true
28290 * @cfg {Number} thumbSize default 300
28291 * @cfg {String} fieldLabel
28292 * @cfg {Number} labelWidth default 4
28293 * @cfg {String} labelAlign (left|top) default left
28294 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28295 * @cfg {Number} labellg set the width of label (1-12)
28296 * @cfg {Number} labelmd set the width of label (1-12)
28297 * @cfg {Number} labelsm set the width of label (1-12)
28298 * @cfg {Number} labelxs set the width of label (1-12)
28301 * Create a new DocumentManager
28302 * @param {Object} config The config object
28305 Roo.bootstrap.DocumentManager = function(config){
28306 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28309 this.delegates = [];
28314 * Fire when initial the DocumentManager
28315 * @param {Roo.bootstrap.DocumentManager} this
28320 * inspect selected file
28321 * @param {Roo.bootstrap.DocumentManager} this
28322 * @param {File} file
28327 * Fire when xhr load exception
28328 * @param {Roo.bootstrap.DocumentManager} this
28329 * @param {XMLHttpRequest} xhr
28331 "exception" : true,
28333 * @event afterupload
28334 * Fire when xhr load exception
28335 * @param {Roo.bootstrap.DocumentManager} this
28336 * @param {XMLHttpRequest} xhr
28338 "afterupload" : true,
28341 * prepare the form data
28342 * @param {Roo.bootstrap.DocumentManager} this
28343 * @param {Object} formData
28348 * Fire when remove the file
28349 * @param {Roo.bootstrap.DocumentManager} this
28350 * @param {Object} file
28355 * Fire after refresh the file
28356 * @param {Roo.bootstrap.DocumentManager} this
28361 * Fire after click the image
28362 * @param {Roo.bootstrap.DocumentManager} this
28363 * @param {Object} file
28368 * Fire when upload a image and editable set to true
28369 * @param {Roo.bootstrap.DocumentManager} this
28370 * @param {Object} file
28374 * @event beforeselectfile
28375 * Fire before select file
28376 * @param {Roo.bootstrap.DocumentManager} this
28378 "beforeselectfile" : true,
28381 * Fire before process file
28382 * @param {Roo.bootstrap.DocumentManager} this
28383 * @param {Object} file
28390 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28399 paramName : 'imageUpload',
28400 toolTipName : 'filename',
28403 labelAlign : 'left',
28413 getAutoCreate : function()
28415 var managerWidget = {
28417 cls : 'roo-document-manager',
28421 cls : 'roo-document-manager-selector',
28426 cls : 'roo-document-manager-uploader',
28430 cls : 'roo-document-manager-upload-btn',
28431 html : '<i class="fa fa-plus"></i>'
28442 cls : 'column col-md-12',
28447 if(this.fieldLabel.length){
28452 cls : 'column col-md-12',
28453 html : this.fieldLabel
28457 cls : 'column col-md-12',
28462 if(this.labelAlign == 'left'){
28467 html : this.fieldLabel
28476 if(this.labelWidth > 12){
28477 content[0].style = "width: " + this.labelWidth + 'px';
28480 if(this.labelWidth < 13 && this.labelmd == 0){
28481 this.labelmd = this.labelWidth;
28484 if(this.labellg > 0){
28485 content[0].cls += ' col-lg-' + this.labellg;
28486 content[1].cls += ' col-lg-' + (12 - this.labellg);
28489 if(this.labelmd > 0){
28490 content[0].cls += ' col-md-' + this.labelmd;
28491 content[1].cls += ' col-md-' + (12 - this.labelmd);
28494 if(this.labelsm > 0){
28495 content[0].cls += ' col-sm-' + this.labelsm;
28496 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28499 if(this.labelxs > 0){
28500 content[0].cls += ' col-xs-' + this.labelxs;
28501 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28509 cls : 'row clearfix',
28517 initEvents : function()
28519 this.managerEl = this.el.select('.roo-document-manager', true).first();
28520 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28522 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28523 this.selectorEl.hide();
28526 this.selectorEl.attr('multiple', 'multiple');
28529 this.selectorEl.on('change', this.onFileSelected, this);
28531 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28532 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28534 this.uploader.on('click', this.onUploaderClick, this);
28536 this.renderProgressDialog();
28540 window.addEventListener("resize", function() { _this.refresh(); } );
28542 this.fireEvent('initial', this);
28545 renderProgressDialog : function()
28549 this.progressDialog = new Roo.bootstrap.Modal({
28550 cls : 'roo-document-manager-progress-dialog',
28551 allow_close : false,
28561 btnclick : function() {
28562 _this.uploadCancel();
28568 this.progressDialog.render(Roo.get(document.body));
28570 this.progress = new Roo.bootstrap.Progress({
28571 cls : 'roo-document-manager-progress',
28576 this.progress.render(this.progressDialog.getChildContainer());
28578 this.progressBar = new Roo.bootstrap.ProgressBar({
28579 cls : 'roo-document-manager-progress-bar',
28582 aria_valuemax : 12,
28586 this.progressBar.render(this.progress.getChildContainer());
28589 onUploaderClick : function(e)
28591 e.preventDefault();
28593 if(this.fireEvent('beforeselectfile', this) != false){
28594 this.selectorEl.dom.click();
28599 onFileSelected : function(e)
28601 e.preventDefault();
28603 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28607 Roo.each(this.selectorEl.dom.files, function(file){
28608 if(this.fireEvent('inspect', this, file) != false){
28609 this.files.push(file);
28619 this.selectorEl.dom.value = '';
28621 if(!this.files.length){
28625 if(this.boxes > 0 && this.files.length > this.boxes){
28626 this.files = this.files.slice(0, this.boxes);
28629 this.uploader.show();
28631 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28632 this.uploader.hide();
28641 Roo.each(this.files, function(file){
28643 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28644 var f = this.renderPreview(file);
28649 if(file.type.indexOf('image') != -1){
28650 this.delegates.push(
28652 _this.process(file);
28653 }).createDelegate(this)
28661 _this.process(file);
28662 }).createDelegate(this)
28667 this.files = files;
28669 this.delegates = this.delegates.concat(docs);
28671 if(!this.delegates.length){
28676 this.progressBar.aria_valuemax = this.delegates.length;
28683 arrange : function()
28685 if(!this.delegates.length){
28686 this.progressDialog.hide();
28691 var delegate = this.delegates.shift();
28693 this.progressDialog.show();
28695 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28697 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28702 refresh : function()
28704 this.uploader.show();
28706 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28707 this.uploader.hide();
28710 Roo.isTouch ? this.closable(false) : this.closable(true);
28712 this.fireEvent('refresh', this);
28715 onRemove : function(e, el, o)
28717 e.preventDefault();
28719 this.fireEvent('remove', this, o);
28723 remove : function(o)
28727 Roo.each(this.files, function(file){
28728 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28737 this.files = files;
28744 Roo.each(this.files, function(file){
28749 file.target.remove();
28758 onClick : function(e, el, o)
28760 e.preventDefault();
28762 this.fireEvent('click', this, o);
28766 closable : function(closable)
28768 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28770 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28782 xhrOnLoad : function(xhr)
28784 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28788 if (xhr.readyState !== 4) {
28790 this.fireEvent('exception', this, xhr);
28794 var response = Roo.decode(xhr.responseText);
28796 if(!response.success){
28798 this.fireEvent('exception', this, xhr);
28802 var file = this.renderPreview(response.data);
28804 this.files.push(file);
28808 this.fireEvent('afterupload', this, xhr);
28812 xhrOnError : function(xhr)
28814 Roo.log('xhr on error');
28816 var response = Roo.decode(xhr.responseText);
28823 process : function(file)
28825 if(this.fireEvent('process', this, file) !== false){
28826 if(this.editable && file.type.indexOf('image') != -1){
28827 this.fireEvent('edit', this, file);
28831 this.uploadStart(file, false);
28838 uploadStart : function(file, crop)
28840 this.xhr = new XMLHttpRequest();
28842 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28847 file.xhr = this.xhr;
28849 this.managerEl.createChild({
28851 cls : 'roo-document-manager-loading',
28855 tooltip : file.name,
28856 cls : 'roo-document-manager-thumb',
28857 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28863 this.xhr.open(this.method, this.url, true);
28866 "Accept": "application/json",
28867 "Cache-Control": "no-cache",
28868 "X-Requested-With": "XMLHttpRequest"
28871 for (var headerName in headers) {
28872 var headerValue = headers[headerName];
28874 this.xhr.setRequestHeader(headerName, headerValue);
28880 this.xhr.onload = function()
28882 _this.xhrOnLoad(_this.xhr);
28885 this.xhr.onerror = function()
28887 _this.xhrOnError(_this.xhr);
28890 var formData = new FormData();
28892 formData.append('returnHTML', 'NO');
28895 formData.append('crop', crop);
28898 formData.append(this.paramName, file, file.name);
28905 if(this.fireEvent('prepare', this, formData, options) != false){
28907 if(options.manually){
28911 this.xhr.send(formData);
28915 this.uploadCancel();
28918 uploadCancel : function()
28924 this.delegates = [];
28926 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28933 renderPreview : function(file)
28935 if(typeof(file.target) != 'undefined' && file.target){
28939 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28941 var previewEl = this.managerEl.createChild({
28943 cls : 'roo-document-manager-preview',
28947 tooltip : file[this.toolTipName],
28948 cls : 'roo-document-manager-thumb',
28949 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28954 html : '<i class="fa fa-times-circle"></i>'
28959 var close = previewEl.select('button.close', true).first();
28961 close.on('click', this.onRemove, this, file);
28963 file.target = previewEl;
28965 var image = previewEl.select('img', true).first();
28969 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28971 image.on('click', this.onClick, this, file);
28977 onPreviewLoad : function(file, image)
28979 if(typeof(file.target) == 'undefined' || !file.target){
28983 var width = image.dom.naturalWidth || image.dom.width;
28984 var height = image.dom.naturalHeight || image.dom.height;
28986 if(width > height){
28987 file.target.addClass('wide');
28991 file.target.addClass('tall');
28996 uploadFromSource : function(file, crop)
28998 this.xhr = new XMLHttpRequest();
29000 this.managerEl.createChild({
29002 cls : 'roo-document-manager-loading',
29006 tooltip : file.name,
29007 cls : 'roo-document-manager-thumb',
29008 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29014 this.xhr.open(this.method, this.url, true);
29017 "Accept": "application/json",
29018 "Cache-Control": "no-cache",
29019 "X-Requested-With": "XMLHttpRequest"
29022 for (var headerName in headers) {
29023 var headerValue = headers[headerName];
29025 this.xhr.setRequestHeader(headerName, headerValue);
29031 this.xhr.onload = function()
29033 _this.xhrOnLoad(_this.xhr);
29036 this.xhr.onerror = function()
29038 _this.xhrOnError(_this.xhr);
29041 var formData = new FormData();
29043 formData.append('returnHTML', 'NO');
29045 formData.append('crop', crop);
29047 if(typeof(file.filename) != 'undefined'){
29048 formData.append('filename', file.filename);
29051 if(typeof(file.mimetype) != 'undefined'){
29052 formData.append('mimetype', file.mimetype);
29057 if(this.fireEvent('prepare', this, formData) != false){
29058 this.xhr.send(formData);
29068 * @class Roo.bootstrap.DocumentViewer
29069 * @extends Roo.bootstrap.Component
29070 * Bootstrap DocumentViewer class
29071 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29072 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29075 * Create a new DocumentViewer
29076 * @param {Object} config The config object
29079 Roo.bootstrap.DocumentViewer = function(config){
29080 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29085 * Fire after initEvent
29086 * @param {Roo.bootstrap.DocumentViewer} this
29092 * @param {Roo.bootstrap.DocumentViewer} this
29097 * Fire after download button
29098 * @param {Roo.bootstrap.DocumentViewer} this
29103 * Fire after trash button
29104 * @param {Roo.bootstrap.DocumentViewer} this
29111 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29113 showDownload : true,
29117 getAutoCreate : function()
29121 cls : 'roo-document-viewer',
29125 cls : 'roo-document-viewer-body',
29129 cls : 'roo-document-viewer-thumb',
29133 cls : 'roo-document-viewer-image'
29141 cls : 'roo-document-viewer-footer',
29144 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29148 cls : 'btn-group roo-document-viewer-download',
29152 cls : 'btn btn-default',
29153 html : '<i class="fa fa-download"></i>'
29159 cls : 'btn-group roo-document-viewer-trash',
29163 cls : 'btn btn-default',
29164 html : '<i class="fa fa-trash"></i>'
29177 initEvents : function()
29179 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29180 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29182 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29183 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29185 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29186 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29188 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29189 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29191 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29192 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29194 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29195 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29197 this.bodyEl.on('click', this.onClick, this);
29198 this.downloadBtn.on('click', this.onDownload, this);
29199 this.trashBtn.on('click', this.onTrash, this);
29201 this.downloadBtn.hide();
29202 this.trashBtn.hide();
29204 if(this.showDownload){
29205 this.downloadBtn.show();
29208 if(this.showTrash){
29209 this.trashBtn.show();
29212 if(!this.showDownload && !this.showTrash) {
29213 this.footerEl.hide();
29218 initial : function()
29220 this.fireEvent('initial', this);
29224 onClick : function(e)
29226 e.preventDefault();
29228 this.fireEvent('click', this);
29231 onDownload : function(e)
29233 e.preventDefault();
29235 this.fireEvent('download', this);
29238 onTrash : function(e)
29240 e.preventDefault();
29242 this.fireEvent('trash', this);
29254 * @class Roo.bootstrap.NavProgressBar
29255 * @extends Roo.bootstrap.Component
29256 * Bootstrap NavProgressBar class
29259 * Create a new nav progress bar
29260 * @param {Object} config The config object
29263 Roo.bootstrap.NavProgressBar = function(config){
29264 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29266 this.bullets = this.bullets || [];
29268 // Roo.bootstrap.NavProgressBar.register(this);
29272 * Fires when the active item changes
29273 * @param {Roo.bootstrap.NavProgressBar} this
29274 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29275 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29282 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29287 getAutoCreate : function()
29289 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29293 cls : 'roo-navigation-bar-group',
29297 cls : 'roo-navigation-top-bar'
29301 cls : 'roo-navigation-bullets-bar',
29305 cls : 'roo-navigation-bar'
29312 cls : 'roo-navigation-bottom-bar'
29322 initEvents: function()
29327 onRender : function(ct, position)
29329 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29331 if(this.bullets.length){
29332 Roo.each(this.bullets, function(b){
29341 addItem : function(cfg)
29343 var item = new Roo.bootstrap.NavProgressItem(cfg);
29345 item.parentId = this.id;
29346 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29349 var top = new Roo.bootstrap.Element({
29351 cls : 'roo-navigation-bar-text'
29354 var bottom = new Roo.bootstrap.Element({
29356 cls : 'roo-navigation-bar-text'
29359 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29360 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29362 var topText = new Roo.bootstrap.Element({
29364 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29367 var bottomText = new Roo.bootstrap.Element({
29369 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29372 topText.onRender(top.el, null);
29373 bottomText.onRender(bottom.el, null);
29376 item.bottomEl = bottom;
29379 this.barItems.push(item);
29384 getActive : function()
29386 var active = false;
29388 Roo.each(this.barItems, function(v){
29390 if (!v.isActive()) {
29402 setActiveItem : function(item)
29406 Roo.each(this.barItems, function(v){
29407 if (v.rid == item.rid) {
29411 if (v.isActive()) {
29412 v.setActive(false);
29417 item.setActive(true);
29419 this.fireEvent('changed', this, item, prev);
29422 getBarItem: function(rid)
29426 Roo.each(this.barItems, function(e) {
29427 if (e.rid != rid) {
29438 indexOfItem : function(item)
29442 Roo.each(this.barItems, function(v, i){
29444 if (v.rid != item.rid) {
29455 setActiveNext : function()
29457 var i = this.indexOfItem(this.getActive());
29459 if (i > this.barItems.length) {
29463 this.setActiveItem(this.barItems[i+1]);
29466 setActivePrev : function()
29468 var i = this.indexOfItem(this.getActive());
29474 this.setActiveItem(this.barItems[i-1]);
29477 format : function()
29479 if(!this.barItems.length){
29483 var width = 100 / this.barItems.length;
29485 Roo.each(this.barItems, function(i){
29486 i.el.setStyle('width', width + '%');
29487 i.topEl.el.setStyle('width', width + '%');
29488 i.bottomEl.el.setStyle('width', width + '%');
29497 * Nav Progress Item
29502 * @class Roo.bootstrap.NavProgressItem
29503 * @extends Roo.bootstrap.Component
29504 * Bootstrap NavProgressItem class
29505 * @cfg {String} rid the reference id
29506 * @cfg {Boolean} active (true|false) Is item active default false
29507 * @cfg {Boolean} disabled (true|false) Is item active default false
29508 * @cfg {String} html
29509 * @cfg {String} position (top|bottom) text position default bottom
29510 * @cfg {String} icon show icon instead of number
29513 * Create a new NavProgressItem
29514 * @param {Object} config The config object
29516 Roo.bootstrap.NavProgressItem = function(config){
29517 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29522 * The raw click event for the entire grid.
29523 * @param {Roo.bootstrap.NavProgressItem} this
29524 * @param {Roo.EventObject} e
29531 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29537 position : 'bottom',
29540 getAutoCreate : function()
29542 var iconCls = 'roo-navigation-bar-item-icon';
29544 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29548 cls: 'roo-navigation-bar-item',
29558 cfg.cls += ' active';
29561 cfg.cls += ' disabled';
29567 disable : function()
29569 this.setDisabled(true);
29572 enable : function()
29574 this.setDisabled(false);
29577 initEvents: function()
29579 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29581 this.iconEl.on('click', this.onClick, this);
29584 onClick : function(e)
29586 e.preventDefault();
29592 if(this.fireEvent('click', this, e) === false){
29596 this.parent().setActiveItem(this);
29599 isActive: function ()
29601 return this.active;
29604 setActive : function(state)
29606 if(this.active == state){
29610 this.active = state;
29613 this.el.addClass('active');
29617 this.el.removeClass('active');
29622 setDisabled : function(state)
29624 if(this.disabled == state){
29628 this.disabled = state;
29631 this.el.addClass('disabled');
29635 this.el.removeClass('disabled');
29638 tooltipEl : function()
29640 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29653 * @class Roo.bootstrap.FieldLabel
29654 * @extends Roo.bootstrap.Component
29655 * Bootstrap FieldLabel class
29656 * @cfg {String} html contents of the element
29657 * @cfg {String} tag tag of the element default label
29658 * @cfg {String} cls class of the element
29659 * @cfg {String} target label target
29660 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29661 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29662 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29663 * @cfg {String} iconTooltip default "This field is required"
29666 * Create a new FieldLabel
29667 * @param {Object} config The config object
29670 Roo.bootstrap.FieldLabel = function(config){
29671 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29676 * Fires after the field has been marked as invalid.
29677 * @param {Roo.form.FieldLabel} this
29678 * @param {String} msg The validation message
29683 * Fires after the field has been validated with no errors.
29684 * @param {Roo.form.FieldLabel} this
29690 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
29697 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29698 validClass : 'text-success fa fa-lg fa-check',
29699 iconTooltip : 'This field is required',
29701 getAutoCreate : function(){
29705 cls : 'roo-bootstrap-field-label ' + this.cls,
29711 tooltip : this.iconTooltip
29723 initEvents: function()
29725 Roo.bootstrap.Element.superclass.initEvents.call(this);
29727 this.iconEl = this.el.select('i', true).first();
29729 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29731 Roo.bootstrap.FieldLabel.register(this);
29735 * Mark this field as valid
29737 markValid : function()
29739 this.iconEl.show();
29741 this.iconEl.removeClass(this.invalidClass);
29743 this.iconEl.addClass(this.validClass);
29745 this.fireEvent('valid', this);
29749 * Mark this field as invalid
29750 * @param {String} msg The validation message
29752 markInvalid : function(msg)
29754 this.iconEl.show();
29756 this.iconEl.removeClass(this.validClass);
29758 this.iconEl.addClass(this.invalidClass);
29760 this.fireEvent('invalid', this, msg);
29766 Roo.apply(Roo.bootstrap.FieldLabel, {
29771 * register a FieldLabel Group
29772 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29774 register : function(label)
29776 if(this.groups.hasOwnProperty(label.target)){
29780 this.groups[label.target] = label;
29784 * fetch a FieldLabel Group based on the target
29785 * @param {string} target
29786 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29788 get: function(target) {
29789 if (typeof(this.groups[target]) == 'undefined') {
29793 return this.groups[target] ;
29802 * page DateSplitField.
29808 * @class Roo.bootstrap.DateSplitField
29809 * @extends Roo.bootstrap.Component
29810 * Bootstrap DateSplitField class
29811 * @cfg {string} fieldLabel - the label associated
29812 * @cfg {Number} labelWidth set the width of label (0-12)
29813 * @cfg {String} labelAlign (top|left)
29814 * @cfg {Boolean} dayAllowBlank (true|false) default false
29815 * @cfg {Boolean} monthAllowBlank (true|false) default false
29816 * @cfg {Boolean} yearAllowBlank (true|false) default false
29817 * @cfg {string} dayPlaceholder
29818 * @cfg {string} monthPlaceholder
29819 * @cfg {string} yearPlaceholder
29820 * @cfg {string} dayFormat default 'd'
29821 * @cfg {string} monthFormat default 'm'
29822 * @cfg {string} yearFormat default 'Y'
29823 * @cfg {Number} labellg set the width of label (1-12)
29824 * @cfg {Number} labelmd set the width of label (1-12)
29825 * @cfg {Number} labelsm set the width of label (1-12)
29826 * @cfg {Number} labelxs set the width of label (1-12)
29830 * Create a new DateSplitField
29831 * @param {Object} config The config object
29834 Roo.bootstrap.DateSplitField = function(config){
29835 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29841 * getting the data of years
29842 * @param {Roo.bootstrap.DateSplitField} this
29843 * @param {Object} years
29848 * getting the data of days
29849 * @param {Roo.bootstrap.DateSplitField} this
29850 * @param {Object} days
29855 * Fires after the field has been marked as invalid.
29856 * @param {Roo.form.Field} this
29857 * @param {String} msg The validation message
29862 * Fires after the field has been validated with no errors.
29863 * @param {Roo.form.Field} this
29869 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
29872 labelAlign : 'top',
29874 dayAllowBlank : false,
29875 monthAllowBlank : false,
29876 yearAllowBlank : false,
29877 dayPlaceholder : '',
29878 monthPlaceholder : '',
29879 yearPlaceholder : '',
29883 isFormField : true,
29889 getAutoCreate : function()
29893 cls : 'row roo-date-split-field-group',
29898 cls : 'form-hidden-field roo-date-split-field-group-value',
29904 var labelCls = 'col-md-12';
29905 var contentCls = 'col-md-4';
29907 if(this.fieldLabel){
29911 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29915 html : this.fieldLabel
29920 if(this.labelAlign == 'left'){
29922 if(this.labelWidth > 12){
29923 label.style = "width: " + this.labelWidth + 'px';
29926 if(this.labelWidth < 13 && this.labelmd == 0){
29927 this.labelmd = this.labelWidth;
29930 if(this.labellg > 0){
29931 labelCls = ' col-lg-' + this.labellg;
29932 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29935 if(this.labelmd > 0){
29936 labelCls = ' col-md-' + this.labelmd;
29937 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29940 if(this.labelsm > 0){
29941 labelCls = ' col-sm-' + this.labelsm;
29942 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29945 if(this.labelxs > 0){
29946 labelCls = ' col-xs-' + this.labelxs;
29947 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29951 label.cls += ' ' + labelCls;
29953 cfg.cn.push(label);
29956 Roo.each(['day', 'month', 'year'], function(t){
29959 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29966 inputEl: function ()
29968 return this.el.select('.roo-date-split-field-group-value', true).first();
29971 onRender : function(ct, position)
29975 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29977 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29979 this.dayField = new Roo.bootstrap.ComboBox({
29980 allowBlank : this.dayAllowBlank,
29981 alwaysQuery : true,
29982 displayField : 'value',
29985 forceSelection : true,
29987 placeholder : this.dayPlaceholder,
29988 selectOnFocus : true,
29989 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29990 triggerAction : 'all',
29992 valueField : 'value',
29993 store : new Roo.data.SimpleStore({
29994 data : (function() {
29996 _this.fireEvent('days', _this, days);
29999 fields : [ 'value' ]
30002 select : function (_self, record, index)
30004 _this.setValue(_this.getValue());
30009 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30011 this.monthField = new Roo.bootstrap.MonthField({
30012 after : '<i class=\"fa fa-calendar\"></i>',
30013 allowBlank : this.monthAllowBlank,
30014 placeholder : this.monthPlaceholder,
30017 render : function (_self)
30019 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30020 e.preventDefault();
30024 select : function (_self, oldvalue, newvalue)
30026 _this.setValue(_this.getValue());
30031 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30033 this.yearField = new Roo.bootstrap.ComboBox({
30034 allowBlank : this.yearAllowBlank,
30035 alwaysQuery : true,
30036 displayField : 'value',
30039 forceSelection : true,
30041 placeholder : this.yearPlaceholder,
30042 selectOnFocus : true,
30043 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30044 triggerAction : 'all',
30046 valueField : 'value',
30047 store : new Roo.data.SimpleStore({
30048 data : (function() {
30050 _this.fireEvent('years', _this, years);
30053 fields : [ 'value' ]
30056 select : function (_self, record, index)
30058 _this.setValue(_this.getValue());
30063 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30066 setValue : function(v, format)
30068 this.inputEl.dom.value = v;
30070 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30072 var d = Date.parseDate(v, f);
30079 this.setDay(d.format(this.dayFormat));
30080 this.setMonth(d.format(this.monthFormat));
30081 this.setYear(d.format(this.yearFormat));
30088 setDay : function(v)
30090 this.dayField.setValue(v);
30091 this.inputEl.dom.value = this.getValue();
30096 setMonth : function(v)
30098 this.monthField.setValue(v, true);
30099 this.inputEl.dom.value = this.getValue();
30104 setYear : function(v)
30106 this.yearField.setValue(v);
30107 this.inputEl.dom.value = this.getValue();
30112 getDay : function()
30114 return this.dayField.getValue();
30117 getMonth : function()
30119 return this.monthField.getValue();
30122 getYear : function()
30124 return this.yearField.getValue();
30127 getValue : function()
30129 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30131 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30141 this.inputEl.dom.value = '';
30146 validate : function()
30148 var d = this.dayField.validate();
30149 var m = this.monthField.validate();
30150 var y = this.yearField.validate();
30155 (!this.dayAllowBlank && !d) ||
30156 (!this.monthAllowBlank && !m) ||
30157 (!this.yearAllowBlank && !y)
30162 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30171 this.markInvalid();
30176 markValid : function()
30179 var label = this.el.select('label', true).first();
30180 var icon = this.el.select('i.fa-star', true).first();
30186 this.fireEvent('valid', this);
30190 * Mark this field as invalid
30191 * @param {String} msg The validation message
30193 markInvalid : function(msg)
30196 var label = this.el.select('label', true).first();
30197 var icon = this.el.select('i.fa-star', true).first();
30199 if(label && !icon){
30200 this.el.select('.roo-date-split-field-label', true).createChild({
30202 cls : 'text-danger fa fa-lg fa-star',
30203 tooltip : 'This field is required',
30204 style : 'margin-right:5px;'
30208 this.fireEvent('invalid', this, msg);
30211 clearInvalid : function()
30213 var label = this.el.select('label', true).first();
30214 var icon = this.el.select('i.fa-star', true).first();
30220 this.fireEvent('valid', this);
30223 getName: function()
30233 * http://masonry.desandro.com
30235 * The idea is to render all the bricks based on vertical width...
30237 * The original code extends 'outlayer' - we might need to use that....
30243 * @class Roo.bootstrap.LayoutMasonry
30244 * @extends Roo.bootstrap.Component
30245 * Bootstrap Layout Masonry class
30248 * Create a new Element
30249 * @param {Object} config The config object
30252 Roo.bootstrap.LayoutMasonry = function(config){
30254 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30258 Roo.bootstrap.LayoutMasonry.register(this);
30264 * Fire after layout the items
30265 * @param {Roo.bootstrap.LayoutMasonry} this
30266 * @param {Roo.EventObject} e
30273 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30276 * @cfg {Boolean} isLayoutInstant = no animation?
30278 isLayoutInstant : false, // needed?
30281 * @cfg {Number} boxWidth width of the columns
30286 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30291 * @cfg {Number} padWidth padding below box..
30296 * @cfg {Number} gutter gutter width..
30301 * @cfg {Number} maxCols maximum number of columns
30307 * @cfg {Boolean} isAutoInitial defalut true
30309 isAutoInitial : true,
30314 * @cfg {Boolean} isHorizontal defalut false
30316 isHorizontal : false,
30318 currentSize : null,
30324 bricks: null, //CompositeElement
30328 _isLayoutInited : false,
30330 // isAlternative : false, // only use for vertical layout...
30333 * @cfg {Number} alternativePadWidth padding below box..
30335 alternativePadWidth : 50,
30337 selectedBrick : [],
30339 getAutoCreate : function(){
30341 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30345 cls: 'blog-masonary-wrapper ' + this.cls,
30347 cls : 'mas-boxes masonary'
30354 getChildContainer: function( )
30356 if (this.boxesEl) {
30357 return this.boxesEl;
30360 this.boxesEl = this.el.select('.mas-boxes').first();
30362 return this.boxesEl;
30366 initEvents : function()
30370 if(this.isAutoInitial){
30371 Roo.log('hook children rendered');
30372 this.on('childrenrendered', function() {
30373 Roo.log('children rendered');
30379 initial : function()
30381 this.selectedBrick = [];
30383 this.currentSize = this.el.getBox(true);
30385 Roo.EventManager.onWindowResize(this.resize, this);
30387 if(!this.isAutoInitial){
30395 //this.layout.defer(500,this);
30399 resize : function()
30401 var cs = this.el.getBox(true);
30404 this.currentSize.width == cs.width &&
30405 this.currentSize.x == cs.x &&
30406 this.currentSize.height == cs.height &&
30407 this.currentSize.y == cs.y
30409 Roo.log("no change in with or X or Y");
30413 this.currentSize = cs;
30419 layout : function()
30421 this._resetLayout();
30423 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30425 this.layoutItems( isInstant );
30427 this._isLayoutInited = true;
30429 this.fireEvent('layout', this);
30433 _resetLayout : function()
30435 if(this.isHorizontal){
30436 this.horizontalMeasureColumns();
30440 this.verticalMeasureColumns();
30444 verticalMeasureColumns : function()
30446 this.getContainerWidth();
30448 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30449 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30453 var boxWidth = this.boxWidth + this.padWidth;
30455 if(this.containerWidth < this.boxWidth){
30456 boxWidth = this.containerWidth
30459 var containerWidth = this.containerWidth;
30461 var cols = Math.floor(containerWidth / boxWidth);
30463 this.cols = Math.max( cols, 1 );
30465 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30467 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30469 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30471 this.colWidth = boxWidth + avail - this.padWidth;
30473 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30474 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30477 horizontalMeasureColumns : function()
30479 this.getContainerWidth();
30481 var boxWidth = this.boxWidth;
30483 if(this.containerWidth < boxWidth){
30484 boxWidth = this.containerWidth;
30487 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30489 this.el.setHeight(boxWidth);
30493 getContainerWidth : function()
30495 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30498 layoutItems : function( isInstant )
30500 Roo.log(this.bricks);
30502 var items = Roo.apply([], this.bricks);
30504 if(this.isHorizontal){
30505 this._horizontalLayoutItems( items , isInstant );
30509 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30510 // this._verticalAlternativeLayoutItems( items , isInstant );
30514 this._verticalLayoutItems( items , isInstant );
30518 _verticalLayoutItems : function ( items , isInstant)
30520 if ( !items || !items.length ) {
30525 ['xs', 'xs', 'xs', 'tall'],
30526 ['xs', 'xs', 'tall'],
30527 ['xs', 'xs', 'sm'],
30528 ['xs', 'xs', 'xs'],
30534 ['sm', 'xs', 'xs'],
30538 ['tall', 'xs', 'xs', 'xs'],
30539 ['tall', 'xs', 'xs'],
30551 Roo.each(items, function(item, k){
30553 switch (item.size) {
30554 // these layouts take up a full box,
30565 boxes.push([item]);
30588 var filterPattern = function(box, length)
30596 var pattern = box.slice(0, length);
30600 Roo.each(pattern, function(i){
30601 format.push(i.size);
30604 Roo.each(standard, function(s){
30606 if(String(s) != String(format)){
30615 if(!match && length == 1){
30620 filterPattern(box, length - 1);
30624 queue.push(pattern);
30626 box = box.slice(length, box.length);
30628 filterPattern(box, 4);
30634 Roo.each(boxes, function(box, k){
30640 if(box.length == 1){
30645 filterPattern(box, 4);
30649 this._processVerticalLayoutQueue( queue, isInstant );
30653 // _verticalAlternativeLayoutItems : function( items , isInstant )
30655 // if ( !items || !items.length ) {
30659 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
30663 _horizontalLayoutItems : function ( items , isInstant)
30665 if ( !items || !items.length || items.length < 3) {
30671 var eItems = items.slice(0, 3);
30673 items = items.slice(3, items.length);
30676 ['xs', 'xs', 'xs', 'wide'],
30677 ['xs', 'xs', 'wide'],
30678 ['xs', 'xs', 'sm'],
30679 ['xs', 'xs', 'xs'],
30685 ['sm', 'xs', 'xs'],
30689 ['wide', 'xs', 'xs', 'xs'],
30690 ['wide', 'xs', 'xs'],
30703 Roo.each(items, function(item, k){
30705 switch (item.size) {
30716 boxes.push([item]);
30740 var filterPattern = function(box, length)
30748 var pattern = box.slice(0, length);
30752 Roo.each(pattern, function(i){
30753 format.push(i.size);
30756 Roo.each(standard, function(s){
30758 if(String(s) != String(format)){
30767 if(!match && length == 1){
30772 filterPattern(box, length - 1);
30776 queue.push(pattern);
30778 box = box.slice(length, box.length);
30780 filterPattern(box, 4);
30786 Roo.each(boxes, function(box, k){
30792 if(box.length == 1){
30797 filterPattern(box, 4);
30804 var pos = this.el.getBox(true);
30808 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30810 var hit_end = false;
30812 Roo.each(queue, function(box){
30816 Roo.each(box, function(b){
30818 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30828 Roo.each(box, function(b){
30830 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30833 mx = Math.max(mx, b.x);
30837 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30841 Roo.each(box, function(b){
30843 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30857 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30860 /** Sets position of item in DOM
30861 * @param {Element} item
30862 * @param {Number} x - horizontal position
30863 * @param {Number} y - vertical position
30864 * @param {Boolean} isInstant - disables transitions
30866 _processVerticalLayoutQueue : function( queue, isInstant )
30868 var pos = this.el.getBox(true);
30873 for (var i = 0; i < this.cols; i++){
30877 Roo.each(queue, function(box, k){
30879 var col = k % this.cols;
30881 Roo.each(box, function(b,kk){
30883 b.el.position('absolute');
30885 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30886 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30888 if(b.size == 'md-left' || b.size == 'md-right'){
30889 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30890 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30893 b.el.setWidth(width);
30894 b.el.setHeight(height);
30896 b.el.select('iframe',true).setSize(width,height);
30900 for (var i = 0; i < this.cols; i++){
30902 if(maxY[i] < maxY[col]){
30907 col = Math.min(col, i);
30911 x = pos.x + col * (this.colWidth + this.padWidth);
30915 var positions = [];
30917 switch (box.length){
30919 positions = this.getVerticalOneBoxColPositions(x, y, box);
30922 positions = this.getVerticalTwoBoxColPositions(x, y, box);
30925 positions = this.getVerticalThreeBoxColPositions(x, y, box);
30928 positions = this.getVerticalFourBoxColPositions(x, y, box);
30934 Roo.each(box, function(b,kk){
30936 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30938 var sz = b.el.getSize();
30940 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30948 for (var i = 0; i < this.cols; i++){
30949 mY = Math.max(mY, maxY[i]);
30952 this.el.setHeight(mY - pos.y);
30956 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30958 // var pos = this.el.getBox(true);
30961 // var maxX = pos.right;
30963 // var maxHeight = 0;
30965 // Roo.each(items, function(item, k){
30969 // item.el.position('absolute');
30971 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30973 // item.el.setWidth(width);
30975 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30977 // item.el.setHeight(height);
30980 // item.el.setXY([x, y], isInstant ? false : true);
30982 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30985 // y = y + height + this.alternativePadWidth;
30987 // maxHeight = maxHeight + height + this.alternativePadWidth;
30991 // this.el.setHeight(maxHeight);
30995 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30997 var pos = this.el.getBox(true);
31002 var maxX = pos.right;
31004 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31006 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31008 Roo.each(queue, function(box, k){
31010 Roo.each(box, function(b, kk){
31012 b.el.position('absolute');
31014 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31015 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31017 if(b.size == 'md-left' || b.size == 'md-right'){
31018 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31019 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31022 b.el.setWidth(width);
31023 b.el.setHeight(height);
31031 var positions = [];
31033 switch (box.length){
31035 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31038 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31041 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31044 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31050 Roo.each(box, function(b,kk){
31052 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31054 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31062 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31064 Roo.each(eItems, function(b,k){
31066 b.size = (k == 0) ? 'sm' : 'xs';
31067 b.x = (k == 0) ? 2 : 1;
31068 b.y = (k == 0) ? 2 : 1;
31070 b.el.position('absolute');
31072 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31074 b.el.setWidth(width);
31076 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31078 b.el.setHeight(height);
31082 var positions = [];
31085 x : maxX - this.unitWidth * 2 - this.gutter,
31090 x : maxX - this.unitWidth,
31091 y : minY + (this.unitWidth + this.gutter) * 2
31095 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31099 Roo.each(eItems, function(b,k){
31101 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31107 getVerticalOneBoxColPositions : function(x, y, box)
31111 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31113 if(box[0].size == 'md-left'){
31117 if(box[0].size == 'md-right'){
31122 x : x + (this.unitWidth + this.gutter) * rand,
31129 getVerticalTwoBoxColPositions : function(x, y, box)
31133 if(box[0].size == 'xs'){
31137 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31141 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31155 x : x + (this.unitWidth + this.gutter) * 2,
31156 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31163 getVerticalThreeBoxColPositions : function(x, y, box)
31167 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31175 x : x + (this.unitWidth + this.gutter) * 1,
31180 x : x + (this.unitWidth + this.gutter) * 2,
31188 if(box[0].size == 'xs' && box[1].size == 'xs'){
31197 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31201 x : x + (this.unitWidth + this.gutter) * 1,
31215 x : x + (this.unitWidth + this.gutter) * 2,
31220 x : x + (this.unitWidth + this.gutter) * 2,
31221 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31228 getVerticalFourBoxColPositions : function(x, y, box)
31232 if(box[0].size == 'xs'){
31241 y : y + (this.unitHeight + this.gutter) * 1
31246 y : y + (this.unitHeight + this.gutter) * 2
31250 x : x + (this.unitWidth + this.gutter) * 1,
31264 x : x + (this.unitWidth + this.gutter) * 2,
31269 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31270 y : y + (this.unitHeight + this.gutter) * 1
31274 x : x + (this.unitWidth + this.gutter) * 2,
31275 y : y + (this.unitWidth + this.gutter) * 2
31282 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31286 if(box[0].size == 'md-left'){
31288 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31295 if(box[0].size == 'md-right'){
31297 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31298 y : minY + (this.unitWidth + this.gutter) * 1
31304 var rand = Math.floor(Math.random() * (4 - box[0].y));
31307 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31308 y : minY + (this.unitWidth + this.gutter) * rand
31315 getHorizontalTwoBoxColPositions : 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[1].x - this.gutter * (box[1].x - 1),
31328 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31336 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31341 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31342 y : minY + (this.unitWidth + this.gutter) * 2
31349 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31353 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31356 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31361 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31362 y : minY + (this.unitWidth + this.gutter) * 1
31366 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31367 y : minY + (this.unitWidth + this.gutter) * 2
31374 if(box[0].size == 'xs' && box[1].size == 'xs'){
31377 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31382 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31387 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31388 y : minY + (this.unitWidth + this.gutter) * 1
31396 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31401 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31402 y : minY + (this.unitWidth + this.gutter) * 2
31406 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31407 y : minY + (this.unitWidth + this.gutter) * 2
31414 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31418 if(box[0].size == 'xs'){
31421 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31426 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31431 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),
31436 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31437 y : minY + (this.unitWidth + this.gutter) * 1
31445 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31450 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31451 y : minY + (this.unitWidth + this.gutter) * 2
31455 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31456 y : minY + (this.unitWidth + this.gutter) * 2
31460 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),
31461 y : minY + (this.unitWidth + this.gutter) * 2
31469 * remove a Masonry Brick
31470 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31472 removeBrick : function(brick_id)
31478 for (var i = 0; i<this.bricks.length; i++) {
31479 if (this.bricks[i].id == brick_id) {
31480 this.bricks.splice(i,1);
31481 this.el.dom.removeChild(Roo.get(brick_id).dom);
31488 * adds a Masonry Brick
31489 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31491 addBrick : function(cfg)
31493 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31494 //this.register(cn);
31495 cn.parentId = this.id;
31496 cn.onRender(this.el, null);
31501 * register a Masonry Brick
31502 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31505 register : function(brick)
31507 this.bricks.push(brick);
31508 brick.masonryId = this.id;
31512 * clear all the Masonry Brick
31514 clearAll : function()
31517 //this.getChildContainer().dom.innerHTML = "";
31518 this.el.dom.innerHTML = '';
31521 getSelected : function()
31523 if (!this.selectedBrick) {
31527 return this.selectedBrick;
31531 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31535 * register a Masonry Layout
31536 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31539 register : function(layout)
31541 this.groups[layout.id] = layout;
31544 * fetch a Masonry Layout based on the masonry layout ID
31545 * @param {string} the masonry layout to add
31546 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31549 get: function(layout_id) {
31550 if (typeof(this.groups[layout_id]) == 'undefined') {
31553 return this.groups[layout_id] ;
31565 * http://masonry.desandro.com
31567 * The idea is to render all the bricks based on vertical width...
31569 * The original code extends 'outlayer' - we might need to use that....
31575 * @class Roo.bootstrap.LayoutMasonryAuto
31576 * @extends Roo.bootstrap.Component
31577 * Bootstrap Layout Masonry class
31580 * Create a new Element
31581 * @param {Object} config The config object
31584 Roo.bootstrap.LayoutMasonryAuto = function(config){
31585 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31588 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31591 * @cfg {Boolean} isFitWidth - resize the width..
31593 isFitWidth : false, // options..
31595 * @cfg {Boolean} isOriginLeft = left align?
31597 isOriginLeft : true,
31599 * @cfg {Boolean} isOriginTop = top align?
31601 isOriginTop : false,
31603 * @cfg {Boolean} isLayoutInstant = no animation?
31605 isLayoutInstant : false, // needed?
31607 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31609 isResizingContainer : true,
31611 * @cfg {Number} columnWidth width of the columns
31617 * @cfg {Number} maxCols maximum number of columns
31622 * @cfg {Number} padHeight padding below box..
31628 * @cfg {Boolean} isAutoInitial defalut true
31631 isAutoInitial : true,
31637 initialColumnWidth : 0,
31638 currentSize : null,
31640 colYs : null, // array.
31647 bricks: null, //CompositeElement
31648 cols : 0, // array?
31649 // element : null, // wrapped now this.el
31650 _isLayoutInited : null,
31653 getAutoCreate : function(){
31657 cls: 'blog-masonary-wrapper ' + this.cls,
31659 cls : 'mas-boxes masonary'
31666 getChildContainer: function( )
31668 if (this.boxesEl) {
31669 return this.boxesEl;
31672 this.boxesEl = this.el.select('.mas-boxes').first();
31674 return this.boxesEl;
31678 initEvents : function()
31682 if(this.isAutoInitial){
31683 Roo.log('hook children rendered');
31684 this.on('childrenrendered', function() {
31685 Roo.log('children rendered');
31692 initial : function()
31694 this.reloadItems();
31696 this.currentSize = this.el.getBox(true);
31698 /// was window resize... - let's see if this works..
31699 Roo.EventManager.onWindowResize(this.resize, this);
31701 if(!this.isAutoInitial){
31706 this.layout.defer(500,this);
31709 reloadItems: function()
31711 this.bricks = this.el.select('.masonry-brick', true);
31713 this.bricks.each(function(b) {
31714 //Roo.log(b.getSize());
31715 if (!b.attr('originalwidth')) {
31716 b.attr('originalwidth', b.getSize().width);
31721 Roo.log(this.bricks.elements.length);
31724 resize : function()
31727 var cs = this.el.getBox(true);
31729 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31730 Roo.log("no change in with or X");
31733 this.currentSize = cs;
31737 layout : function()
31740 this._resetLayout();
31741 //this._manageStamps();
31743 // don't animate first layout
31744 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31745 this.layoutItems( isInstant );
31747 // flag for initalized
31748 this._isLayoutInited = true;
31751 layoutItems : function( isInstant )
31753 //var items = this._getItemsForLayout( this.items );
31754 // original code supports filtering layout items.. we just ignore it..
31756 this._layoutItems( this.bricks , isInstant );
31758 this._postLayout();
31760 _layoutItems : function ( items , isInstant)
31762 //this.fireEvent( 'layout', this, items );
31765 if ( !items || !items.elements.length ) {
31766 // no items, emit event with empty array
31771 items.each(function(item) {
31772 Roo.log("layout item");
31774 // get x/y object from method
31775 var position = this._getItemLayoutPosition( item );
31777 position.item = item;
31778 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31779 queue.push( position );
31782 this._processLayoutQueue( queue );
31784 /** Sets position of item in DOM
31785 * @param {Element} item
31786 * @param {Number} x - horizontal position
31787 * @param {Number} y - vertical position
31788 * @param {Boolean} isInstant - disables transitions
31790 _processLayoutQueue : function( queue )
31792 for ( var i=0, len = queue.length; i < len; i++ ) {
31793 var obj = queue[i];
31794 obj.item.position('absolute');
31795 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31801 * Any logic you want to do after each layout,
31802 * i.e. size the container
31804 _postLayout : function()
31806 this.resizeContainer();
31809 resizeContainer : function()
31811 if ( !this.isResizingContainer ) {
31814 var size = this._getContainerSize();
31816 this.el.setSize(size.width,size.height);
31817 this.boxesEl.setSize(size.width,size.height);
31823 _resetLayout : function()
31825 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31826 this.colWidth = this.el.getWidth();
31827 //this.gutter = this.el.getWidth();
31829 this.measureColumns();
31835 this.colYs.push( 0 );
31841 measureColumns : function()
31843 this.getContainerWidth();
31844 // if columnWidth is 0, default to outerWidth of first item
31845 if ( !this.columnWidth ) {
31846 var firstItem = this.bricks.first();
31847 Roo.log(firstItem);
31848 this.columnWidth = this.containerWidth;
31849 if (firstItem && firstItem.attr('originalwidth') ) {
31850 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31852 // columnWidth fall back to item of first element
31853 Roo.log("set column width?");
31854 this.initialColumnWidth = this.columnWidth ;
31856 // if first elem has no width, default to size of container
31861 if (this.initialColumnWidth) {
31862 this.columnWidth = this.initialColumnWidth;
31867 // column width is fixed at the top - however if container width get's smaller we should
31870 // this bit calcs how man columns..
31872 var columnWidth = this.columnWidth += this.gutter;
31874 // calculate columns
31875 var containerWidth = this.containerWidth + this.gutter;
31877 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31878 // fix rounding errors, typically with gutters
31879 var excess = columnWidth - containerWidth % columnWidth;
31882 // if overshoot is less than a pixel, round up, otherwise floor it
31883 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31884 cols = Math[ mathMethod ]( cols );
31885 this.cols = Math.max( cols, 1 );
31886 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31888 // padding positioning..
31889 var totalColWidth = this.cols * this.columnWidth;
31890 var padavail = this.containerWidth - totalColWidth;
31891 // so for 2 columns - we need 3 'pads'
31893 var padNeeded = (1+this.cols) * this.padWidth;
31895 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31897 this.columnWidth += padExtra
31898 //this.padWidth = Math.floor(padavail / ( this.cols));
31900 // adjust colum width so that padding is fixed??
31902 // we have 3 columns ... total = width * 3
31903 // we have X left over... that should be used by
31905 //if (this.expandC) {
31913 getContainerWidth : function()
31915 /* // container is parent if fit width
31916 var container = this.isFitWidth ? this.element.parentNode : this.element;
31917 // check that this.size and size are there
31918 // IE8 triggers resize on body size change, so they might not be
31920 var size = getSize( container ); //FIXME
31921 this.containerWidth = size && size.innerWidth; //FIXME
31924 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31928 _getItemLayoutPosition : function( item ) // what is item?
31930 // we resize the item to our columnWidth..
31932 item.setWidth(this.columnWidth);
31933 item.autoBoxAdjust = false;
31935 var sz = item.getSize();
31937 // how many columns does this brick span
31938 var remainder = this.containerWidth % this.columnWidth;
31940 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31941 // round if off by 1 pixel, otherwise use ceil
31942 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
31943 colSpan = Math.min( colSpan, this.cols );
31945 // normally this should be '1' as we dont' currently allow multi width columns..
31947 var colGroup = this._getColGroup( colSpan );
31948 // get the minimum Y value from the columns
31949 var minimumY = Math.min.apply( Math, colGroup );
31950 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31952 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
31954 // position the brick
31956 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31957 y: this.currentSize.y + minimumY + this.padHeight
31961 // apply setHeight to necessary columns
31962 var setHeight = minimumY + sz.height + this.padHeight;
31963 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31965 var setSpan = this.cols + 1 - colGroup.length;
31966 for ( var i = 0; i < setSpan; i++ ) {
31967 this.colYs[ shortColIndex + i ] = setHeight ;
31974 * @param {Number} colSpan - number of columns the element spans
31975 * @returns {Array} colGroup
31977 _getColGroup : function( colSpan )
31979 if ( colSpan < 2 ) {
31980 // if brick spans only one column, use all the column Ys
31985 // how many different places could this brick fit horizontally
31986 var groupCount = this.cols + 1 - colSpan;
31987 // for each group potential horizontal position
31988 for ( var i = 0; i < groupCount; i++ ) {
31989 // make an array of colY values for that one group
31990 var groupColYs = this.colYs.slice( i, i + colSpan );
31991 // and get the max value of the array
31992 colGroup[i] = Math.max.apply( Math, groupColYs );
31997 _manageStamp : function( stamp )
31999 var stampSize = stamp.getSize();
32000 var offset = stamp.getBox();
32001 // get the columns that this stamp affects
32002 var firstX = this.isOriginLeft ? offset.x : offset.right;
32003 var lastX = firstX + stampSize.width;
32004 var firstCol = Math.floor( firstX / this.columnWidth );
32005 firstCol = Math.max( 0, firstCol );
32007 var lastCol = Math.floor( lastX / this.columnWidth );
32008 // lastCol should not go over if multiple of columnWidth #425
32009 lastCol -= lastX % this.columnWidth ? 0 : 1;
32010 lastCol = Math.min( this.cols - 1, lastCol );
32012 // set colYs to bottom of the stamp
32013 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32016 for ( var i = firstCol; i <= lastCol; i++ ) {
32017 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32022 _getContainerSize : function()
32024 this.maxY = Math.max.apply( Math, this.colYs );
32029 if ( this.isFitWidth ) {
32030 size.width = this._getContainerFitWidth();
32036 _getContainerFitWidth : function()
32038 var unusedCols = 0;
32039 // count unused columns
32042 if ( this.colYs[i] !== 0 ) {
32047 // fit container to columns that have been used
32048 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32051 needsResizeLayout : function()
32053 var previousWidth = this.containerWidth;
32054 this.getContainerWidth();
32055 return previousWidth !== this.containerWidth;
32070 * @class Roo.bootstrap.MasonryBrick
32071 * @extends Roo.bootstrap.Component
32072 * Bootstrap MasonryBrick class
32075 * Create a new MasonryBrick
32076 * @param {Object} config The config object
32079 Roo.bootstrap.MasonryBrick = function(config){
32081 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32083 Roo.bootstrap.MasonryBrick.register(this);
32089 * When a MasonryBrick is clcik
32090 * @param {Roo.bootstrap.MasonryBrick} this
32091 * @param {Roo.EventObject} e
32097 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32100 * @cfg {String} title
32104 * @cfg {String} html
32108 * @cfg {String} bgimage
32112 * @cfg {String} videourl
32116 * @cfg {String} cls
32120 * @cfg {String} href
32124 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32129 * @cfg {String} placetitle (center|bottom)
32134 * @cfg {Boolean} isFitContainer defalut true
32136 isFitContainer : true,
32139 * @cfg {Boolean} preventDefault defalut false
32141 preventDefault : false,
32144 * @cfg {Boolean} inverse defalut false
32146 maskInverse : false,
32148 getAutoCreate : function()
32150 if(!this.isFitContainer){
32151 return this.getSplitAutoCreate();
32154 var cls = 'masonry-brick masonry-brick-full';
32156 if(this.href.length){
32157 cls += ' masonry-brick-link';
32160 if(this.bgimage.length){
32161 cls += ' masonry-brick-image';
32164 if(this.maskInverse){
32165 cls += ' mask-inverse';
32168 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32169 cls += ' enable-mask';
32173 cls += ' masonry-' + this.size + '-brick';
32176 if(this.placetitle.length){
32178 switch (this.placetitle) {
32180 cls += ' masonry-center-title';
32183 cls += ' masonry-bottom-title';
32190 if(!this.html.length && !this.bgimage.length){
32191 cls += ' masonry-center-title';
32194 if(!this.html.length && this.bgimage.length){
32195 cls += ' masonry-bottom-title';
32200 cls += ' ' + this.cls;
32204 tag: (this.href.length) ? 'a' : 'div',
32209 cls: 'masonry-brick-mask'
32213 cls: 'masonry-brick-paragraph',
32219 if(this.href.length){
32220 cfg.href = this.href;
32223 var cn = cfg.cn[1].cn;
32225 if(this.title.length){
32228 cls: 'masonry-brick-title',
32233 if(this.html.length){
32236 cls: 'masonry-brick-text',
32241 if (!this.title.length && !this.html.length) {
32242 cfg.cn[1].cls += ' hide';
32245 if(this.bgimage.length){
32248 cls: 'masonry-brick-image-view',
32253 if(this.videourl.length){
32254 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32255 // youtube support only?
32258 cls: 'masonry-brick-image-view',
32261 allowfullscreen : true
32269 getSplitAutoCreate : function()
32271 var cls = 'masonry-brick masonry-brick-split';
32273 if(this.href.length){
32274 cls += ' masonry-brick-link';
32277 if(this.bgimage.length){
32278 cls += ' masonry-brick-image';
32282 cls += ' masonry-' + this.size + '-brick';
32285 switch (this.placetitle) {
32287 cls += ' masonry-center-title';
32290 cls += ' masonry-bottom-title';
32293 if(!this.bgimage.length){
32294 cls += ' masonry-center-title';
32297 if(this.bgimage.length){
32298 cls += ' masonry-bottom-title';
32304 cls += ' ' + this.cls;
32308 tag: (this.href.length) ? 'a' : 'div',
32313 cls: 'masonry-brick-split-head',
32317 cls: 'masonry-brick-paragraph',
32324 cls: 'masonry-brick-split-body',
32330 if(this.href.length){
32331 cfg.href = this.href;
32334 if(this.title.length){
32335 cfg.cn[0].cn[0].cn.push({
32337 cls: 'masonry-brick-title',
32342 if(this.html.length){
32343 cfg.cn[1].cn.push({
32345 cls: 'masonry-brick-text',
32350 if(this.bgimage.length){
32351 cfg.cn[0].cn.push({
32353 cls: 'masonry-brick-image-view',
32358 if(this.videourl.length){
32359 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32360 // youtube support only?
32361 cfg.cn[0].cn.cn.push({
32363 cls: 'masonry-brick-image-view',
32366 allowfullscreen : true
32373 initEvents: function()
32375 switch (this.size) {
32408 this.el.on('touchstart', this.onTouchStart, this);
32409 this.el.on('touchmove', this.onTouchMove, this);
32410 this.el.on('touchend', this.onTouchEnd, this);
32411 this.el.on('contextmenu', this.onContextMenu, this);
32413 this.el.on('mouseenter' ,this.enter, this);
32414 this.el.on('mouseleave', this.leave, this);
32415 this.el.on('click', this.onClick, this);
32418 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32419 this.parent().bricks.push(this);
32424 onClick: function(e, el)
32426 var time = this.endTimer - this.startTimer;
32427 // Roo.log(e.preventDefault());
32430 e.preventDefault();
32435 if(!this.preventDefault){
32439 e.preventDefault();
32441 if (this.activcClass != '') {
32442 this.selectBrick();
32445 this.fireEvent('click', this);
32448 enter: function(e, el)
32450 e.preventDefault();
32452 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32456 if(this.bgimage.length && this.html.length){
32457 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32461 leave: function(e, el)
32463 e.preventDefault();
32465 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32469 if(this.bgimage.length && this.html.length){
32470 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32474 onTouchStart: function(e, el)
32476 // e.preventDefault();
32478 this.touchmoved = false;
32480 if(!this.isFitContainer){
32484 if(!this.bgimage.length || !this.html.length){
32488 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32490 this.timer = new Date().getTime();
32494 onTouchMove: function(e, el)
32496 this.touchmoved = true;
32499 onContextMenu : function(e,el)
32501 e.preventDefault();
32502 e.stopPropagation();
32506 onTouchEnd: function(e, el)
32508 // e.preventDefault();
32510 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32517 if(!this.bgimage.length || !this.html.length){
32519 if(this.href.length){
32520 window.location.href = this.href;
32526 if(!this.isFitContainer){
32530 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32532 window.location.href = this.href;
32535 //selection on single brick only
32536 selectBrick : function() {
32538 if (!this.parentId) {
32542 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32543 var index = m.selectedBrick.indexOf(this.id);
32546 m.selectedBrick.splice(index,1);
32547 this.el.removeClass(this.activeClass);
32551 for(var i = 0; i < m.selectedBrick.length; i++) {
32552 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32553 b.el.removeClass(b.activeClass);
32556 m.selectedBrick = [];
32558 m.selectedBrick.push(this.id);
32559 this.el.addClass(this.activeClass);
32565 Roo.apply(Roo.bootstrap.MasonryBrick, {
32568 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32570 * register a Masonry Brick
32571 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32574 register : function(brick)
32576 //this.groups[brick.id] = brick;
32577 this.groups.add(brick.id, brick);
32580 * fetch a masonry brick based on the masonry brick ID
32581 * @param {string} the masonry brick to add
32582 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32585 get: function(brick_id)
32587 // if (typeof(this.groups[brick_id]) == 'undefined') {
32590 // return this.groups[brick_id] ;
32592 if(this.groups.key(brick_id)) {
32593 return this.groups.key(brick_id);
32611 * @class Roo.bootstrap.Brick
32612 * @extends Roo.bootstrap.Component
32613 * Bootstrap Brick class
32616 * Create a new Brick
32617 * @param {Object} config The config object
32620 Roo.bootstrap.Brick = function(config){
32621 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32627 * When a Brick is click
32628 * @param {Roo.bootstrap.Brick} this
32629 * @param {Roo.EventObject} e
32635 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32638 * @cfg {String} title
32642 * @cfg {String} html
32646 * @cfg {String} bgimage
32650 * @cfg {String} cls
32654 * @cfg {String} href
32658 * @cfg {String} video
32662 * @cfg {Boolean} square
32666 getAutoCreate : function()
32668 var cls = 'roo-brick';
32670 if(this.href.length){
32671 cls += ' roo-brick-link';
32674 if(this.bgimage.length){
32675 cls += ' roo-brick-image';
32678 if(!this.html.length && !this.bgimage.length){
32679 cls += ' roo-brick-center-title';
32682 if(!this.html.length && this.bgimage.length){
32683 cls += ' roo-brick-bottom-title';
32687 cls += ' ' + this.cls;
32691 tag: (this.href.length) ? 'a' : 'div',
32696 cls: 'roo-brick-paragraph',
32702 if(this.href.length){
32703 cfg.href = this.href;
32706 var cn = cfg.cn[0].cn;
32708 if(this.title.length){
32711 cls: 'roo-brick-title',
32716 if(this.html.length){
32719 cls: 'roo-brick-text',
32726 if(this.bgimage.length){
32729 cls: 'roo-brick-image-view',
32737 initEvents: function()
32739 if(this.title.length || this.html.length){
32740 this.el.on('mouseenter' ,this.enter, this);
32741 this.el.on('mouseleave', this.leave, this);
32744 Roo.EventManager.onWindowResize(this.resize, this);
32746 if(this.bgimage.length){
32747 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32748 this.imageEl.on('load', this.onImageLoad, this);
32755 onImageLoad : function()
32760 resize : function()
32762 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32764 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32766 if(this.bgimage.length){
32767 var image = this.el.select('.roo-brick-image-view', true).first();
32769 image.setWidth(paragraph.getWidth());
32772 image.setHeight(paragraph.getWidth());
32775 this.el.setHeight(image.getHeight());
32776 paragraph.setHeight(image.getHeight());
32782 enter: function(e, el)
32784 e.preventDefault();
32786 if(this.bgimage.length){
32787 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32788 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32792 leave: function(e, el)
32794 e.preventDefault();
32796 if(this.bgimage.length){
32797 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32798 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32814 * @class Roo.bootstrap.NumberField
32815 * @extends Roo.bootstrap.Input
32816 * Bootstrap NumberField class
32822 * Create a new NumberField
32823 * @param {Object} config The config object
32826 Roo.bootstrap.NumberField = function(config){
32827 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32830 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32833 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32835 allowDecimals : true,
32837 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32839 decimalSeparator : ".",
32841 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32843 decimalPrecision : 2,
32845 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32847 allowNegative : true,
32849 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32851 minValue : Number.NEGATIVE_INFINITY,
32853 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32855 maxValue : Number.MAX_VALUE,
32857 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32859 minText : "The minimum value for this field is {0}",
32861 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32863 maxText : "The maximum value for this field is {0}",
32865 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
32866 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32868 nanText : "{0} is not a valid number",
32870 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32875 initEvents : function()
32877 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32879 var allowed = "0123456789";
32881 if(this.allowDecimals){
32882 allowed += this.decimalSeparator;
32885 if(this.allowNegative){
32889 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32891 var keyPress = function(e){
32893 var k = e.getKey();
32895 var c = e.getCharCode();
32898 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32899 allowed.indexOf(String.fromCharCode(c)) === -1
32905 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32909 if(allowed.indexOf(String.fromCharCode(c)) === -1){
32914 this.el.on("keypress", keyPress, this);
32917 validateValue : function(value)
32920 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32924 var num = this.parseValue(value);
32927 this.markInvalid(String.format(this.nanText, value));
32931 if(num < this.minValue){
32932 this.markInvalid(String.format(this.minText, this.minValue));
32936 if(num > this.maxValue){
32937 this.markInvalid(String.format(this.maxText, this.maxValue));
32944 getValue : function()
32946 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32949 parseValue : function(value)
32951 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32952 return isNaN(value) ? '' : value;
32955 fixPrecision : function(value)
32957 var nan = isNaN(value);
32959 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32960 return nan ? '' : value;
32962 return parseFloat(value).toFixed(this.decimalPrecision);
32965 setValue : function(v)
32967 v = this.fixPrecision(v);
32968 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32971 decimalPrecisionFcn : function(v)
32973 return Math.floor(v);
32976 beforeBlur : function()
32982 var v = this.parseValue(this.getRawValue());
32997 * @class Roo.bootstrap.DocumentSlider
32998 * @extends Roo.bootstrap.Component
32999 * Bootstrap DocumentSlider class
33002 * Create a new DocumentViewer
33003 * @param {Object} config The config object
33006 Roo.bootstrap.DocumentSlider = function(config){
33007 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33014 * Fire after initEvent
33015 * @param {Roo.bootstrap.DocumentSlider} this
33020 * Fire after update
33021 * @param {Roo.bootstrap.DocumentSlider} this
33027 * @param {Roo.bootstrap.DocumentSlider} this
33033 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33039 getAutoCreate : function()
33043 cls : 'roo-document-slider',
33047 cls : 'roo-document-slider-header',
33051 cls : 'roo-document-slider-header-title'
33057 cls : 'roo-document-slider-body',
33061 cls : 'roo-document-slider-prev',
33065 cls : 'fa fa-chevron-left'
33071 cls : 'roo-document-slider-thumb',
33075 cls : 'roo-document-slider-image'
33081 cls : 'roo-document-slider-next',
33085 cls : 'fa fa-chevron-right'
33097 initEvents : function()
33099 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33100 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33102 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33103 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33105 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33106 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33108 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33109 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33111 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33112 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33114 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33115 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33117 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33118 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33120 this.thumbEl.on('click', this.onClick, this);
33122 this.prevIndicator.on('click', this.prev, this);
33124 this.nextIndicator.on('click', this.next, this);
33128 initial : function()
33130 if(this.files.length){
33131 this.indicator = 1;
33135 this.fireEvent('initial', this);
33138 update : function()
33140 this.imageEl.attr('src', this.files[this.indicator - 1]);
33142 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33144 this.prevIndicator.show();
33146 if(this.indicator == 1){
33147 this.prevIndicator.hide();
33150 this.nextIndicator.show();
33152 if(this.indicator == this.files.length){
33153 this.nextIndicator.hide();
33156 this.thumbEl.scrollTo('top');
33158 this.fireEvent('update', this);
33161 onClick : function(e)
33163 e.preventDefault();
33165 this.fireEvent('click', this);
33170 e.preventDefault();
33172 this.indicator = Math.max(1, this.indicator - 1);
33179 e.preventDefault();
33181 this.indicator = Math.min(this.files.length, this.indicator + 1);
33195 * @class Roo.bootstrap.RadioSet
33196 * @extends Roo.bootstrap.Input
33197 * Bootstrap RadioSet class
33198 * @cfg {String} indicatorpos (left|right) default left
33199 * @cfg {Boolean} inline (true|false) inline the element (default true)
33200 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33202 * Create a new RadioSet
33203 * @param {Object} config The config object
33206 Roo.bootstrap.RadioSet = function(config){
33208 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33212 Roo.bootstrap.RadioSet.register(this);
33217 * Fires when the element is checked or unchecked.
33218 * @param {Roo.bootstrap.RadioSet} this This radio
33219 * @param {Roo.bootstrap.Radio} item The checked item
33226 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33234 indicatorpos : 'left',
33236 getAutoCreate : function()
33240 cls : 'roo-radio-set-label',
33244 html : this.fieldLabel
33249 if(this.indicatorpos == 'left'){
33252 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33253 tooltip : 'This field is required'
33258 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33259 tooltip : 'This field is required'
33265 cls : 'roo-radio-set-items'
33268 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33270 if (align === 'left' && this.fieldLabel.length) {
33273 cls : "roo-radio-set-right",
33279 if(this.labelWidth > 12){
33280 label.style = "width: " + this.labelWidth + 'px';
33283 if(this.labelWidth < 13 && this.labelmd == 0){
33284 this.labelmd = this.labelWidth;
33287 if(this.labellg > 0){
33288 label.cls += ' col-lg-' + this.labellg;
33289 items.cls += ' col-lg-' + (12 - this.labellg);
33292 if(this.labelmd > 0){
33293 label.cls += ' col-md-' + this.labelmd;
33294 items.cls += ' col-md-' + (12 - this.labelmd);
33297 if(this.labelsm > 0){
33298 label.cls += ' col-sm-' + this.labelsm;
33299 items.cls += ' col-sm-' + (12 - this.labelsm);
33302 if(this.labelxs > 0){
33303 label.cls += ' col-xs-' + this.labelxs;
33304 items.cls += ' col-xs-' + (12 - this.labelxs);
33310 cls : 'roo-radio-set',
33314 cls : 'roo-radio-set-input',
33317 value : this.value ? this.value : ''
33324 if(this.weight.length){
33325 cfg.cls += ' roo-radio-' + this.weight;
33329 cfg.cls += ' roo-radio-set-inline';
33333 ['xs','sm','md','lg'].map(function(size){
33334 if (settings[size]) {
33335 cfg.cls += ' col-' + size + '-' + settings[size];
33343 initEvents : function()
33345 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33346 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33348 if(!this.fieldLabel.length){
33349 this.labelEl.hide();
33352 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33353 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33355 this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33356 this.indicatorEl().hide();
33358 this.originalValue = this.getValue();
33362 inputEl: function ()
33364 return this.el.select('.roo-radio-set-input', true).first();
33367 getChildContainer : function()
33369 return this.itemsEl;
33372 register : function(item)
33374 this.radioes.push(item);
33378 validate : function()
33382 Roo.each(this.radioes, function(i){
33391 if(this.allowBlank) {
33395 if(this.disabled || valid){
33400 this.markInvalid();
33405 markValid : function()
33407 if(this.labelEl.isVisible(true)){
33408 this.indicatorEl().hide();
33411 this.el.removeClass([this.invalidClass, this.validClass]);
33412 this.el.addClass(this.validClass);
33414 this.fireEvent('valid', this);
33417 markInvalid : function(msg)
33419 if(this.allowBlank || this.disabled){
33423 if(this.labelEl.isVisible(true)){
33424 this.indicatorEl().show();
33427 this.el.removeClass([this.invalidClass, this.validClass]);
33428 this.el.addClass(this.invalidClass);
33430 this.fireEvent('invalid', this, msg);
33434 setValue : function(v, suppressEvent)
33438 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33441 Roo.each(this.radioes, function(i){
33444 i.el.removeClass('checked');
33446 if(i.value === v || i.value.toString() === v.toString()){
33448 i.el.addClass('checked');
33450 if(suppressEvent !== true){
33451 this.fireEvent('check', this, i);
33460 clearInvalid : function(){
33462 if(!this.el || this.preventMark){
33466 this.el.removeClass([this.invalidClass]);
33468 this.fireEvent('valid', this);
33473 Roo.apply(Roo.bootstrap.RadioSet, {
33477 register : function(set)
33479 this.groups[set.name] = set;
33482 get: function(name)
33484 if (typeof(this.groups[name]) == 'undefined') {
33488 return this.groups[name] ;
33494 * Ext JS Library 1.1.1
33495 * Copyright(c) 2006-2007, Ext JS, LLC.
33497 * Originally Released Under LGPL - original licence link has changed is not relivant.
33500 * <script type="text/javascript">
33505 * @class Roo.bootstrap.SplitBar
33506 * @extends Roo.util.Observable
33507 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33511 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33512 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33513 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33514 split.minSize = 100;
33515 split.maxSize = 600;
33516 split.animate = true;
33517 split.on('moved', splitterMoved);
33520 * Create a new SplitBar
33521 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33522 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33523 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33524 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33525 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33526 position of the SplitBar).
33528 Roo.bootstrap.SplitBar = function(cfg){
33533 // dragElement : elm
33534 // resizingElement: el,
33536 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33537 // placement : Roo.bootstrap.SplitBar.LEFT ,
33538 // existingProxy ???
33541 this.el = Roo.get(cfg.dragElement, true);
33542 this.el.dom.unselectable = "on";
33544 this.resizingEl = Roo.get(cfg.resizingElement, true);
33548 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33549 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33552 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33555 * The minimum size of the resizing element. (Defaults to 0)
33561 * The maximum size of the resizing element. (Defaults to 2000)
33564 this.maxSize = 2000;
33567 * Whether to animate the transition to the new size
33570 this.animate = false;
33573 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33576 this.useShim = false;
33581 if(!cfg.existingProxy){
33583 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33585 this.proxy = Roo.get(cfg.existingProxy).dom;
33588 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33591 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33594 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33597 this.dragSpecs = {};
33600 * @private The adapter to use to positon and resize elements
33602 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33603 this.adapter.init(this);
33605 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33607 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33608 this.el.addClass("roo-splitbar-h");
33611 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33612 this.el.addClass("roo-splitbar-v");
33618 * Fires when the splitter is moved (alias for {@link #event-moved})
33619 * @param {Roo.bootstrap.SplitBar} this
33620 * @param {Number} newSize the new width or height
33625 * Fires when the splitter is moved
33626 * @param {Roo.bootstrap.SplitBar} this
33627 * @param {Number} newSize the new width or height
33631 * @event beforeresize
33632 * Fires before the splitter is dragged
33633 * @param {Roo.bootstrap.SplitBar} this
33635 "beforeresize" : true,
33637 "beforeapply" : true
33640 Roo.util.Observable.call(this);
33643 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33644 onStartProxyDrag : function(x, y){
33645 this.fireEvent("beforeresize", this);
33647 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
33649 o.enableDisplayMode("block");
33650 // all splitbars share the same overlay
33651 Roo.bootstrap.SplitBar.prototype.overlay = o;
33653 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33654 this.overlay.show();
33655 Roo.get(this.proxy).setDisplayed("block");
33656 var size = this.adapter.getElementSize(this);
33657 this.activeMinSize = this.getMinimumSize();;
33658 this.activeMaxSize = this.getMaximumSize();;
33659 var c1 = size - this.activeMinSize;
33660 var c2 = Math.max(this.activeMaxSize - size, 0);
33661 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33662 this.dd.resetConstraints();
33663 this.dd.setXConstraint(
33664 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
33665 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33667 this.dd.setYConstraint(0, 0);
33669 this.dd.resetConstraints();
33670 this.dd.setXConstraint(0, 0);
33671 this.dd.setYConstraint(
33672 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
33673 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33676 this.dragSpecs.startSize = size;
33677 this.dragSpecs.startPoint = [x, y];
33678 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33682 * @private Called after the drag operation by the DDProxy
33684 onEndProxyDrag : function(e){
33685 Roo.get(this.proxy).setDisplayed(false);
33686 var endPoint = Roo.lib.Event.getXY(e);
33688 this.overlay.hide();
33691 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33692 newSize = this.dragSpecs.startSize +
33693 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33694 endPoint[0] - this.dragSpecs.startPoint[0] :
33695 this.dragSpecs.startPoint[0] - endPoint[0]
33698 newSize = this.dragSpecs.startSize +
33699 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33700 endPoint[1] - this.dragSpecs.startPoint[1] :
33701 this.dragSpecs.startPoint[1] - endPoint[1]
33704 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33705 if(newSize != this.dragSpecs.startSize){
33706 if(this.fireEvent('beforeapply', this, newSize) !== false){
33707 this.adapter.setElementSize(this, newSize);
33708 this.fireEvent("moved", this, newSize);
33709 this.fireEvent("resize", this, newSize);
33715 * Get the adapter this SplitBar uses
33716 * @return The adapter object
33718 getAdapter : function(){
33719 return this.adapter;
33723 * Set the adapter this SplitBar uses
33724 * @param {Object} adapter A SplitBar adapter object
33726 setAdapter : function(adapter){
33727 this.adapter = adapter;
33728 this.adapter.init(this);
33732 * Gets the minimum size for the resizing element
33733 * @return {Number} The minimum size
33735 getMinimumSize : function(){
33736 return this.minSize;
33740 * Sets the minimum size for the resizing element
33741 * @param {Number} minSize The minimum size
33743 setMinimumSize : function(minSize){
33744 this.minSize = minSize;
33748 * Gets the maximum size for the resizing element
33749 * @return {Number} The maximum size
33751 getMaximumSize : function(){
33752 return this.maxSize;
33756 * Sets the maximum size for the resizing element
33757 * @param {Number} maxSize The maximum size
33759 setMaximumSize : function(maxSize){
33760 this.maxSize = maxSize;
33764 * Sets the initialize size for the resizing element
33765 * @param {Number} size The initial size
33767 setCurrentSize : function(size){
33768 var oldAnimate = this.animate;
33769 this.animate = false;
33770 this.adapter.setElementSize(this, size);
33771 this.animate = oldAnimate;
33775 * Destroy this splitbar.
33776 * @param {Boolean} removeEl True to remove the element
33778 destroy : function(removeEl){
33780 this.shim.remove();
33783 this.proxy.parentNode.removeChild(this.proxy);
33791 * @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.
33793 Roo.bootstrap.SplitBar.createProxy = function(dir){
33794 var proxy = new Roo.Element(document.createElement("div"));
33795 proxy.unselectable();
33796 var cls = 'roo-splitbar-proxy';
33797 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33798 document.body.appendChild(proxy.dom);
33803 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33804 * Default Adapter. It assumes the splitter and resizing element are not positioned
33805 * elements and only gets/sets the width of the element. Generally used for table based layouts.
33807 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33810 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33811 // do nothing for now
33812 init : function(s){
33816 * Called before drag operations to get the current size of the resizing element.
33817 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33819 getElementSize : function(s){
33820 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33821 return s.resizingEl.getWidth();
33823 return s.resizingEl.getHeight();
33828 * Called after drag operations to set the size of the resizing element.
33829 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33830 * @param {Number} newSize The new size to set
33831 * @param {Function} onComplete A function to be invoked when resizing is complete
33833 setElementSize : function(s, newSize, onComplete){
33834 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33836 s.resizingEl.setWidth(newSize);
33838 onComplete(s, newSize);
33841 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33846 s.resizingEl.setHeight(newSize);
33848 onComplete(s, newSize);
33851 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33858 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33859 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33860 * Adapter that moves the splitter element to align with the resized sizing element.
33861 * Used with an absolute positioned SplitBar.
33862 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33863 * document.body, make sure you assign an id to the body element.
33865 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33866 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33867 this.container = Roo.get(container);
33870 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33871 init : function(s){
33872 this.basic.init(s);
33875 getElementSize : function(s){
33876 return this.basic.getElementSize(s);
33879 setElementSize : function(s, newSize, onComplete){
33880 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33883 moveSplitter : function(s){
33884 var yes = Roo.bootstrap.SplitBar;
33885 switch(s.placement){
33887 s.el.setX(s.resizingEl.getRight());
33890 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33893 s.el.setY(s.resizingEl.getBottom());
33896 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33903 * Orientation constant - Create a vertical SplitBar
33907 Roo.bootstrap.SplitBar.VERTICAL = 1;
33910 * Orientation constant - Create a horizontal SplitBar
33914 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33917 * Placement constant - The resizing element is to the left of the splitter element
33921 Roo.bootstrap.SplitBar.LEFT = 1;
33924 * Placement constant - The resizing element is to the right of the splitter element
33928 Roo.bootstrap.SplitBar.RIGHT = 2;
33931 * Placement constant - The resizing element is positioned above the splitter element
33935 Roo.bootstrap.SplitBar.TOP = 3;
33938 * Placement constant - The resizing element is positioned under splitter element
33942 Roo.bootstrap.SplitBar.BOTTOM = 4;
33943 Roo.namespace("Roo.bootstrap.layout");/*
33945 * Ext JS Library 1.1.1
33946 * Copyright(c) 2006-2007, Ext JS, LLC.
33948 * Originally Released Under LGPL - original licence link has changed is not relivant.
33951 * <script type="text/javascript">
33955 * @class Roo.bootstrap.layout.Manager
33956 * @extends Roo.bootstrap.Component
33957 * Base class for layout managers.
33959 Roo.bootstrap.layout.Manager = function(config)
33961 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33967 /** false to disable window resize monitoring @type Boolean */
33968 this.monitorWindowResize = true;
33973 * Fires when a layout is performed.
33974 * @param {Roo.LayoutManager} this
33978 * @event regionresized
33979 * Fires when the user resizes a region.
33980 * @param {Roo.LayoutRegion} region The resized region
33981 * @param {Number} newSize The new size (width for east/west, height for north/south)
33983 "regionresized" : true,
33985 * @event regioncollapsed
33986 * Fires when a region is collapsed.
33987 * @param {Roo.LayoutRegion} region The collapsed region
33989 "regioncollapsed" : true,
33991 * @event regionexpanded
33992 * Fires when a region is expanded.
33993 * @param {Roo.LayoutRegion} region The expanded region
33995 "regionexpanded" : true
33997 this.updating = false;
34000 this.el = Roo.get(config.el);
34006 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34011 monitorWindowResize : true,
34017 onRender : function(ct, position)
34020 this.el = Roo.get(ct);
34023 //this.fireEvent('render',this);
34027 initEvents: function()
34031 // ie scrollbar fix
34032 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34033 document.body.scroll = "no";
34034 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34035 this.el.position('relative');
34037 this.id = this.el.id;
34038 this.el.addClass("roo-layout-container");
34039 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34040 if(this.el.dom != document.body ) {
34041 this.el.on('resize', this.layout,this);
34042 this.el.on('show', this.layout,this);
34048 * Returns true if this layout is currently being updated
34049 * @return {Boolean}
34051 isUpdating : function(){
34052 return this.updating;
34056 * Suspend the LayoutManager from doing auto-layouts while
34057 * making multiple add or remove calls
34059 beginUpdate : function(){
34060 this.updating = true;
34064 * Restore auto-layouts and optionally disable the manager from performing a layout
34065 * @param {Boolean} noLayout true to disable a layout update
34067 endUpdate : function(noLayout){
34068 this.updating = false;
34074 layout: function(){
34078 onRegionResized : function(region, newSize){
34079 this.fireEvent("regionresized", region, newSize);
34083 onRegionCollapsed : function(region){
34084 this.fireEvent("regioncollapsed", region);
34087 onRegionExpanded : function(region){
34088 this.fireEvent("regionexpanded", region);
34092 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34093 * performs box-model adjustments.
34094 * @return {Object} The size as an object {width: (the width), height: (the height)}
34096 getViewSize : function()
34099 if(this.el.dom != document.body){
34100 size = this.el.getSize();
34102 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34104 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34105 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34110 * Returns the Element this layout is bound to.
34111 * @return {Roo.Element}
34113 getEl : function(){
34118 * Returns the specified region.
34119 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34120 * @return {Roo.LayoutRegion}
34122 getRegion : function(target){
34123 return this.regions[target.toLowerCase()];
34126 onWindowResize : function(){
34127 if(this.monitorWindowResize){
34134 * Ext JS Library 1.1.1
34135 * Copyright(c) 2006-2007, Ext JS, LLC.
34137 * Originally Released Under LGPL - original licence link has changed is not relivant.
34140 * <script type="text/javascript">
34143 * @class Roo.bootstrap.layout.Border
34144 * @extends Roo.bootstrap.layout.Manager
34145 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34146 * please see: examples/bootstrap/nested.html<br><br>
34148 <b>The container the layout is rendered into can be either the body element or any other element.
34149 If it is not the body element, the container needs to either be an absolute positioned element,
34150 or you will need to add "position:relative" to the css of the container. You will also need to specify
34151 the container size if it is not the body element.</b>
34154 * Create a new Border
34155 * @param {Object} config Configuration options
34157 Roo.bootstrap.layout.Border = function(config){
34158 config = config || {};
34159 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34163 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34164 if(config[region]){
34165 config[region].region = region;
34166 this.addRegion(config[region]);
34172 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34174 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34176 * Creates and adds a new region if it doesn't already exist.
34177 * @param {String} target The target region key (north, south, east, west or center).
34178 * @param {Object} config The regions config object
34179 * @return {BorderLayoutRegion} The new region
34181 addRegion : function(config)
34183 if(!this.regions[config.region]){
34184 var r = this.factory(config);
34185 this.bindRegion(r);
34187 return this.regions[config.region];
34191 bindRegion : function(r){
34192 this.regions[r.config.region] = r;
34194 r.on("visibilitychange", this.layout, this);
34195 r.on("paneladded", this.layout, this);
34196 r.on("panelremoved", this.layout, this);
34197 r.on("invalidated", this.layout, this);
34198 r.on("resized", this.onRegionResized, this);
34199 r.on("collapsed", this.onRegionCollapsed, this);
34200 r.on("expanded", this.onRegionExpanded, this);
34204 * Performs a layout update.
34206 layout : function()
34208 if(this.updating) {
34212 // render all the rebions if they have not been done alreayd?
34213 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34214 if(this.regions[region] && !this.regions[region].bodyEl){
34215 this.regions[region].onRender(this.el)
34219 var size = this.getViewSize();
34220 var w = size.width;
34221 var h = size.height;
34226 //var x = 0, y = 0;
34228 var rs = this.regions;
34229 var north = rs["north"];
34230 var south = rs["south"];
34231 var west = rs["west"];
34232 var east = rs["east"];
34233 var center = rs["center"];
34234 //if(this.hideOnLayout){ // not supported anymore
34235 //c.el.setStyle("display", "none");
34237 if(north && north.isVisible()){
34238 var b = north.getBox();
34239 var m = north.getMargins();
34240 b.width = w - (m.left+m.right);
34243 centerY = b.height + b.y + m.bottom;
34244 centerH -= centerY;
34245 north.updateBox(this.safeBox(b));
34247 if(south && south.isVisible()){
34248 var b = south.getBox();
34249 var m = south.getMargins();
34250 b.width = w - (m.left+m.right);
34252 var totalHeight = (b.height + m.top + m.bottom);
34253 b.y = h - totalHeight + m.top;
34254 centerH -= totalHeight;
34255 south.updateBox(this.safeBox(b));
34257 if(west && west.isVisible()){
34258 var b = west.getBox();
34259 var m = west.getMargins();
34260 b.height = centerH - (m.top+m.bottom);
34262 b.y = centerY + m.top;
34263 var totalWidth = (b.width + m.left + m.right);
34264 centerX += totalWidth;
34265 centerW -= totalWidth;
34266 west.updateBox(this.safeBox(b));
34268 if(east && east.isVisible()){
34269 var b = east.getBox();
34270 var m = east.getMargins();
34271 b.height = centerH - (m.top+m.bottom);
34272 var totalWidth = (b.width + m.left + m.right);
34273 b.x = w - totalWidth + m.left;
34274 b.y = centerY + m.top;
34275 centerW -= totalWidth;
34276 east.updateBox(this.safeBox(b));
34279 var m = center.getMargins();
34281 x: centerX + m.left,
34282 y: centerY + m.top,
34283 width: centerW - (m.left+m.right),
34284 height: centerH - (m.top+m.bottom)
34286 //if(this.hideOnLayout){
34287 //center.el.setStyle("display", "block");
34289 center.updateBox(this.safeBox(centerBox));
34292 this.fireEvent("layout", this);
34296 safeBox : function(box){
34297 box.width = Math.max(0, box.width);
34298 box.height = Math.max(0, box.height);
34303 * Adds a ContentPanel (or subclass) to this layout.
34304 * @param {String} target The target region key (north, south, east, west or center).
34305 * @param {Roo.ContentPanel} panel The panel to add
34306 * @return {Roo.ContentPanel} The added panel
34308 add : function(target, panel){
34310 target = target.toLowerCase();
34311 return this.regions[target].add(panel);
34315 * Remove a ContentPanel (or subclass) to this layout.
34316 * @param {String} target The target region key (north, south, east, west or center).
34317 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34318 * @return {Roo.ContentPanel} The removed panel
34320 remove : function(target, panel){
34321 target = target.toLowerCase();
34322 return this.regions[target].remove(panel);
34326 * Searches all regions for a panel with the specified id
34327 * @param {String} panelId
34328 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34330 findPanel : function(panelId){
34331 var rs = this.regions;
34332 for(var target in rs){
34333 if(typeof rs[target] != "function"){
34334 var p = rs[target].getPanel(panelId);
34344 * Searches all regions for a panel with the specified id and activates (shows) it.
34345 * @param {String/ContentPanel} panelId The panels id or the panel itself
34346 * @return {Roo.ContentPanel} The shown panel or null
34348 showPanel : function(panelId) {
34349 var rs = this.regions;
34350 for(var target in rs){
34351 var r = rs[target];
34352 if(typeof r != "function"){
34353 if(r.hasPanel(panelId)){
34354 return r.showPanel(panelId);
34362 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34363 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34366 restoreState : function(provider){
34368 provider = Roo.state.Manager;
34370 var sm = new Roo.LayoutStateManager();
34371 sm.init(this, provider);
34377 * Adds a xtype elements to the layout.
34381 xtype : 'ContentPanel',
34388 xtype : 'NestedLayoutPanel',
34394 items : [ ... list of content panels or nested layout panels.. ]
34398 * @param {Object} cfg Xtype definition of item to add.
34400 addxtype : function(cfg)
34402 // basically accepts a pannel...
34403 // can accept a layout region..!?!?
34404 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34407 // theory? children can only be panels??
34409 //if (!cfg.xtype.match(/Panel$/)) {
34414 if (typeof(cfg.region) == 'undefined') {
34415 Roo.log("Failed to add Panel, region was not set");
34419 var region = cfg.region;
34425 xitems = cfg.items;
34432 case 'Content': // ContentPanel (el, cfg)
34433 case 'Scroll': // ContentPanel (el, cfg)
34435 cfg.autoCreate = true;
34436 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34438 // var el = this.el.createChild();
34439 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34442 this.add(region, ret);
34446 case 'TreePanel': // our new panel!
34447 cfg.el = this.el.createChild();
34448 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34449 this.add(region, ret);
34454 // create a new Layout (which is a Border Layout...
34456 var clayout = cfg.layout;
34457 clayout.el = this.el.createChild();
34458 clayout.items = clayout.items || [];
34462 // replace this exitems with the clayout ones..
34463 xitems = clayout.items;
34465 // force background off if it's in center...
34466 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34467 cfg.background = false;
34469 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34472 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34473 //console.log('adding nested layout panel ' + cfg.toSource());
34474 this.add(region, ret);
34475 nb = {}; /// find first...
34480 // needs grid and region
34482 //var el = this.getRegion(region).el.createChild();
34484 *var el = this.el.createChild();
34485 // create the grid first...
34486 cfg.grid.container = el;
34487 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34490 if (region == 'center' && this.active ) {
34491 cfg.background = false;
34494 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34496 this.add(region, ret);
34498 if (cfg.background) {
34499 // render grid on panel activation (if panel background)
34500 ret.on('activate', function(gp) {
34501 if (!gp.grid.rendered) {
34502 // gp.grid.render(el);
34506 // cfg.grid.render(el);
34512 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34513 // it was the old xcomponent building that caused this before.
34514 // espeically if border is the top element in the tree.
34524 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34526 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34527 this.add(region, ret);
34531 throw "Can not add '" + cfg.xtype + "' to Border";
34537 this.beginUpdate();
34541 Roo.each(xitems, function(i) {
34542 region = nb && i.region ? i.region : false;
34544 var add = ret.addxtype(i);
34547 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34548 if (!i.background) {
34549 abn[region] = nb[region] ;
34556 // make the last non-background panel active..
34557 //if (nb) { Roo.log(abn); }
34560 for(var r in abn) {
34561 region = this.getRegion(r);
34563 // tried using nb[r], but it does not work..
34565 region.showPanel(abn[r]);
34576 factory : function(cfg)
34579 var validRegions = Roo.bootstrap.layout.Border.regions;
34581 var target = cfg.region;
34584 var r = Roo.bootstrap.layout;
34588 return new r.North(cfg);
34590 return new r.South(cfg);
34592 return new r.East(cfg);
34594 return new r.West(cfg);
34596 return new r.Center(cfg);
34598 throw 'Layout region "'+target+'" not supported.';
34605 * Ext JS Library 1.1.1
34606 * Copyright(c) 2006-2007, Ext JS, LLC.
34608 * Originally Released Under LGPL - original licence link has changed is not relivant.
34611 * <script type="text/javascript">
34615 * @class Roo.bootstrap.layout.Basic
34616 * @extends Roo.util.Observable
34617 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34618 * and does not have a titlebar, tabs or any other features. All it does is size and position
34619 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34620 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34621 * @cfg {string} region the region that it inhabits..
34622 * @cfg {bool} skipConfig skip config?
34626 Roo.bootstrap.layout.Basic = function(config){
34628 this.mgr = config.mgr;
34630 this.position = config.region;
34632 var skipConfig = config.skipConfig;
34636 * @scope Roo.BasicLayoutRegion
34640 * @event beforeremove
34641 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34642 * @param {Roo.LayoutRegion} this
34643 * @param {Roo.ContentPanel} panel The panel
34644 * @param {Object} e The cancel event object
34646 "beforeremove" : true,
34648 * @event invalidated
34649 * Fires when the layout for this region is changed.
34650 * @param {Roo.LayoutRegion} this
34652 "invalidated" : true,
34654 * @event visibilitychange
34655 * Fires when this region is shown or hidden
34656 * @param {Roo.LayoutRegion} this
34657 * @param {Boolean} visibility true or false
34659 "visibilitychange" : true,
34661 * @event paneladded
34662 * Fires when a panel is added.
34663 * @param {Roo.LayoutRegion} this
34664 * @param {Roo.ContentPanel} panel The panel
34666 "paneladded" : true,
34668 * @event panelremoved
34669 * Fires when a panel is removed.
34670 * @param {Roo.LayoutRegion} this
34671 * @param {Roo.ContentPanel} panel The panel
34673 "panelremoved" : true,
34675 * @event beforecollapse
34676 * Fires when this region before collapse.
34677 * @param {Roo.LayoutRegion} this
34679 "beforecollapse" : true,
34682 * Fires when this region is collapsed.
34683 * @param {Roo.LayoutRegion} this
34685 "collapsed" : true,
34688 * Fires when this region is expanded.
34689 * @param {Roo.LayoutRegion} this
34694 * Fires when this region is slid into view.
34695 * @param {Roo.LayoutRegion} this
34697 "slideshow" : true,
34700 * Fires when this region slides out of view.
34701 * @param {Roo.LayoutRegion} this
34703 "slidehide" : true,
34705 * @event panelactivated
34706 * Fires when a panel is activated.
34707 * @param {Roo.LayoutRegion} this
34708 * @param {Roo.ContentPanel} panel The activated panel
34710 "panelactivated" : true,
34713 * Fires when the user resizes this region.
34714 * @param {Roo.LayoutRegion} this
34715 * @param {Number} newSize The new size (width for east/west, height for north/south)
34719 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34720 this.panels = new Roo.util.MixedCollection();
34721 this.panels.getKey = this.getPanelId.createDelegate(this);
34723 this.activePanel = null;
34724 // ensure listeners are added...
34726 if (config.listeners || config.events) {
34727 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34728 listeners : config.listeners || {},
34729 events : config.events || {}
34733 if(skipConfig !== true){
34734 this.applyConfig(config);
34738 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34740 getPanelId : function(p){
34744 applyConfig : function(config){
34745 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34746 this.config = config;
34751 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34752 * the width, for horizontal (north, south) the height.
34753 * @param {Number} newSize The new width or height
34755 resizeTo : function(newSize){
34756 var el = this.el ? this.el :
34757 (this.activePanel ? this.activePanel.getEl() : null);
34759 switch(this.position){
34762 el.setWidth(newSize);
34763 this.fireEvent("resized", this, newSize);
34767 el.setHeight(newSize);
34768 this.fireEvent("resized", this, newSize);
34774 getBox : function(){
34775 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34778 getMargins : function(){
34779 return this.margins;
34782 updateBox : function(box){
34784 var el = this.activePanel.getEl();
34785 el.dom.style.left = box.x + "px";
34786 el.dom.style.top = box.y + "px";
34787 this.activePanel.setSize(box.width, box.height);
34791 * Returns the container element for this region.
34792 * @return {Roo.Element}
34794 getEl : function(){
34795 return this.activePanel;
34799 * Returns true if this region is currently visible.
34800 * @return {Boolean}
34802 isVisible : function(){
34803 return this.activePanel ? true : false;
34806 setActivePanel : function(panel){
34807 panel = this.getPanel(panel);
34808 if(this.activePanel && this.activePanel != panel){
34809 this.activePanel.setActiveState(false);
34810 this.activePanel.getEl().setLeftTop(-10000,-10000);
34812 this.activePanel = panel;
34813 panel.setActiveState(true);
34815 panel.setSize(this.box.width, this.box.height);
34817 this.fireEvent("panelactivated", this, panel);
34818 this.fireEvent("invalidated");
34822 * Show the specified panel.
34823 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34824 * @return {Roo.ContentPanel} The shown panel or null
34826 showPanel : function(panel){
34827 panel = this.getPanel(panel);
34829 this.setActivePanel(panel);
34835 * Get the active panel for this region.
34836 * @return {Roo.ContentPanel} The active panel or null
34838 getActivePanel : function(){
34839 return this.activePanel;
34843 * Add the passed ContentPanel(s)
34844 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34845 * @return {Roo.ContentPanel} The panel added (if only one was added)
34847 add : function(panel){
34848 if(arguments.length > 1){
34849 for(var i = 0, len = arguments.length; i < len; i++) {
34850 this.add(arguments[i]);
34854 if(this.hasPanel(panel)){
34855 this.showPanel(panel);
34858 var el = panel.getEl();
34859 if(el.dom.parentNode != this.mgr.el.dom){
34860 this.mgr.el.dom.appendChild(el.dom);
34862 if(panel.setRegion){
34863 panel.setRegion(this);
34865 this.panels.add(panel);
34866 el.setStyle("position", "absolute");
34867 if(!panel.background){
34868 this.setActivePanel(panel);
34869 if(this.config.initialSize && this.panels.getCount()==1){
34870 this.resizeTo(this.config.initialSize);
34873 this.fireEvent("paneladded", this, panel);
34878 * Returns true if the panel is in this region.
34879 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34880 * @return {Boolean}
34882 hasPanel : function(panel){
34883 if(typeof panel == "object"){ // must be panel obj
34884 panel = panel.getId();
34886 return this.getPanel(panel) ? true : false;
34890 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34891 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34892 * @param {Boolean} preservePanel Overrides the config preservePanel option
34893 * @return {Roo.ContentPanel} The panel that was removed
34895 remove : function(panel, preservePanel){
34896 panel = this.getPanel(panel);
34901 this.fireEvent("beforeremove", this, panel, e);
34902 if(e.cancel === true){
34905 var panelId = panel.getId();
34906 this.panels.removeKey(panelId);
34911 * Returns the panel specified or null if it's not in this region.
34912 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34913 * @return {Roo.ContentPanel}
34915 getPanel : function(id){
34916 if(typeof id == "object"){ // must be panel obj
34919 return this.panels.get(id);
34923 * Returns this regions position (north/south/east/west/center).
34926 getPosition: function(){
34927 return this.position;
34931 * Ext JS Library 1.1.1
34932 * Copyright(c) 2006-2007, Ext JS, LLC.
34934 * Originally Released Under LGPL - original licence link has changed is not relivant.
34937 * <script type="text/javascript">
34941 * @class Roo.bootstrap.layout.Region
34942 * @extends Roo.bootstrap.layout.Basic
34943 * This class represents a region in a layout manager.
34945 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34946 * @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})
34947 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
34948 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
34949 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
34950 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
34951 * @cfg {String} title The title for the region (overrides panel titles)
34952 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
34953 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34954 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
34955 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34956 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
34957 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34958 * the space available, similar to FireFox 1.5 tabs (defaults to false)
34959 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
34960 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
34961 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
34963 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
34964 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
34965 * @cfg {Boolean} disableTabTips True to disable tab tooltips
34966 * @cfg {Number} width For East/West panels
34967 * @cfg {Number} height For North/South panels
34968 * @cfg {Boolean} split To show the splitter
34969 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
34971 * @cfg {string} cls Extra CSS classes to add to region
34973 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34974 * @cfg {string} region the region that it inhabits..
34977 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
34978 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
34980 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
34981 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
34982 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
34984 Roo.bootstrap.layout.Region = function(config)
34986 this.applyConfig(config);
34988 var mgr = config.mgr;
34989 var pos = config.region;
34990 config.skipConfig = true;
34991 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34994 this.onRender(mgr.el);
34997 this.visible = true;
34998 this.collapsed = false;
34999 this.unrendered_panels = [];
35002 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35004 position: '', // set by wrapper (eg. north/south etc..)
35005 unrendered_panels : null, // unrendered panels.
35006 createBody : function(){
35007 /** This region's body element
35008 * @type Roo.Element */
35009 this.bodyEl = this.el.createChild({
35011 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35015 onRender: function(ctr, pos)
35017 var dh = Roo.DomHelper;
35018 /** This region's container element
35019 * @type Roo.Element */
35020 this.el = dh.append(ctr.dom, {
35022 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35024 /** This region's title element
35025 * @type Roo.Element */
35027 this.titleEl = dh.append(this.el.dom,
35030 unselectable: "on",
35031 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35033 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35034 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35037 this.titleEl.enableDisplayMode();
35038 /** This region's title text element
35039 * @type HTMLElement */
35040 this.titleTextEl = this.titleEl.dom.firstChild;
35041 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35043 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35044 this.closeBtn.enableDisplayMode();
35045 this.closeBtn.on("click", this.closeClicked, this);
35046 this.closeBtn.hide();
35048 this.createBody(this.config);
35049 if(this.config.hideWhenEmpty){
35051 this.on("paneladded", this.validateVisibility, this);
35052 this.on("panelremoved", this.validateVisibility, this);
35054 if(this.autoScroll){
35055 this.bodyEl.setStyle("overflow", "auto");
35057 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35059 //if(c.titlebar !== false){
35060 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35061 this.titleEl.hide();
35063 this.titleEl.show();
35064 if(this.config.title){
35065 this.titleTextEl.innerHTML = this.config.title;
35069 if(this.config.collapsed){
35070 this.collapse(true);
35072 if(this.config.hidden){
35076 if (this.unrendered_panels && this.unrendered_panels.length) {
35077 for (var i =0;i< this.unrendered_panels.length; i++) {
35078 this.add(this.unrendered_panels[i]);
35080 this.unrendered_panels = null;
35086 applyConfig : function(c)
35089 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35090 var dh = Roo.DomHelper;
35091 if(c.titlebar !== false){
35092 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35093 this.collapseBtn.on("click", this.collapse, this);
35094 this.collapseBtn.enableDisplayMode();
35096 if(c.showPin === true || this.showPin){
35097 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35098 this.stickBtn.enableDisplayMode();
35099 this.stickBtn.on("click", this.expand, this);
35100 this.stickBtn.hide();
35105 /** This region's collapsed element
35106 * @type Roo.Element */
35109 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35110 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35113 if(c.floatable !== false){
35114 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35115 this.collapsedEl.on("click", this.collapseClick, this);
35118 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35119 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35120 id: "message", unselectable: "on", style:{"float":"left"}});
35121 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35123 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35124 this.expandBtn.on("click", this.expand, this);
35128 if(this.collapseBtn){
35129 this.collapseBtn.setVisible(c.collapsible == true);
35132 this.cmargins = c.cmargins || this.cmargins ||
35133 (this.position == "west" || this.position == "east" ?
35134 {top: 0, left: 2, right:2, bottom: 0} :
35135 {top: 2, left: 0, right:0, bottom: 2});
35137 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35140 this.bottomTabs = c.tabPosition != "top";
35142 this.autoScroll = c.autoScroll || false;
35147 this.duration = c.duration || .30;
35148 this.slideDuration = c.slideDuration || .45;
35153 * Returns true if this region is currently visible.
35154 * @return {Boolean}
35156 isVisible : function(){
35157 return this.visible;
35161 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35162 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35164 //setCollapsedTitle : function(title){
35165 // title = title || " ";
35166 // if(this.collapsedTitleTextEl){
35167 // this.collapsedTitleTextEl.innerHTML = title;
35171 getBox : function(){
35173 // if(!this.collapsed){
35174 b = this.el.getBox(false, true);
35176 // b = this.collapsedEl.getBox(false, true);
35181 getMargins : function(){
35182 return this.margins;
35183 //return this.collapsed ? this.cmargins : this.margins;
35186 highlight : function(){
35187 this.el.addClass("x-layout-panel-dragover");
35190 unhighlight : function(){
35191 this.el.removeClass("x-layout-panel-dragover");
35194 updateBox : function(box)
35196 if (!this.bodyEl) {
35197 return; // not rendered yet..
35201 if(!this.collapsed){
35202 this.el.dom.style.left = box.x + "px";
35203 this.el.dom.style.top = box.y + "px";
35204 this.updateBody(box.width, box.height);
35206 this.collapsedEl.dom.style.left = box.x + "px";
35207 this.collapsedEl.dom.style.top = box.y + "px";
35208 this.collapsedEl.setSize(box.width, box.height);
35211 this.tabs.autoSizeTabs();
35215 updateBody : function(w, h)
35218 this.el.setWidth(w);
35219 w -= this.el.getBorderWidth("rl");
35220 if(this.config.adjustments){
35221 w += this.config.adjustments[0];
35224 if(h !== null && h > 0){
35225 this.el.setHeight(h);
35226 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35227 h -= this.el.getBorderWidth("tb");
35228 if(this.config.adjustments){
35229 h += this.config.adjustments[1];
35231 this.bodyEl.setHeight(h);
35233 h = this.tabs.syncHeight(h);
35236 if(this.panelSize){
35237 w = w !== null ? w : this.panelSize.width;
35238 h = h !== null ? h : this.panelSize.height;
35240 if(this.activePanel){
35241 var el = this.activePanel.getEl();
35242 w = w !== null ? w : el.getWidth();
35243 h = h !== null ? h : el.getHeight();
35244 this.panelSize = {width: w, height: h};
35245 this.activePanel.setSize(w, h);
35247 if(Roo.isIE && this.tabs){
35248 this.tabs.el.repaint();
35253 * Returns the container element for this region.
35254 * @return {Roo.Element}
35256 getEl : function(){
35261 * Hides this region.
35264 //if(!this.collapsed){
35265 this.el.dom.style.left = "-2000px";
35268 // this.collapsedEl.dom.style.left = "-2000px";
35269 // this.collapsedEl.hide();
35271 this.visible = false;
35272 this.fireEvent("visibilitychange", this, false);
35276 * Shows this region if it was previously hidden.
35279 //if(!this.collapsed){
35282 // this.collapsedEl.show();
35284 this.visible = true;
35285 this.fireEvent("visibilitychange", this, true);
35288 closeClicked : function(){
35289 if(this.activePanel){
35290 this.remove(this.activePanel);
35294 collapseClick : function(e){
35296 e.stopPropagation();
35299 e.stopPropagation();
35305 * Collapses this region.
35306 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35309 collapse : function(skipAnim, skipCheck = false){
35310 if(this.collapsed) {
35314 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35316 this.collapsed = true;
35318 this.split.el.hide();
35320 if(this.config.animate && skipAnim !== true){
35321 this.fireEvent("invalidated", this);
35322 this.animateCollapse();
35324 this.el.setLocation(-20000,-20000);
35326 this.collapsedEl.show();
35327 this.fireEvent("collapsed", this);
35328 this.fireEvent("invalidated", this);
35334 animateCollapse : function(){
35339 * Expands this region if it was previously collapsed.
35340 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35341 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35344 expand : function(e, skipAnim){
35346 e.stopPropagation();
35348 if(!this.collapsed || this.el.hasActiveFx()) {
35352 this.afterSlideIn();
35355 this.collapsed = false;
35356 if(this.config.animate && skipAnim !== true){
35357 this.animateExpand();
35361 this.split.el.show();
35363 this.collapsedEl.setLocation(-2000,-2000);
35364 this.collapsedEl.hide();
35365 this.fireEvent("invalidated", this);
35366 this.fireEvent("expanded", this);
35370 animateExpand : function(){
35374 initTabs : function()
35376 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35378 var ts = new Roo.bootstrap.panel.Tabs({
35379 el: this.bodyEl.dom,
35380 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35381 disableTooltips: this.config.disableTabTips,
35382 toolbar : this.config.toolbar
35385 if(this.config.hideTabs){
35386 ts.stripWrap.setDisplayed(false);
35389 ts.resizeTabs = this.config.resizeTabs === true;
35390 ts.minTabWidth = this.config.minTabWidth || 40;
35391 ts.maxTabWidth = this.config.maxTabWidth || 250;
35392 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35393 ts.monitorResize = false;
35394 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35395 ts.bodyEl.addClass('roo-layout-tabs-body');
35396 this.panels.each(this.initPanelAsTab, this);
35399 initPanelAsTab : function(panel){
35400 var ti = this.tabs.addTab(
35404 this.config.closeOnTab && panel.isClosable(),
35407 if(panel.tabTip !== undefined){
35408 ti.setTooltip(panel.tabTip);
35410 ti.on("activate", function(){
35411 this.setActivePanel(panel);
35414 if(this.config.closeOnTab){
35415 ti.on("beforeclose", function(t, e){
35417 this.remove(panel);
35421 panel.tabItem = ti;
35426 updatePanelTitle : function(panel, title)
35428 if(this.activePanel == panel){
35429 this.updateTitle(title);
35432 var ti = this.tabs.getTab(panel.getEl().id);
35434 if(panel.tabTip !== undefined){
35435 ti.setTooltip(panel.tabTip);
35440 updateTitle : function(title){
35441 if(this.titleTextEl && !this.config.title){
35442 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35446 setActivePanel : function(panel)
35448 panel = this.getPanel(panel);
35449 if(this.activePanel && this.activePanel != panel){
35450 this.activePanel.setActiveState(false);
35452 this.activePanel = panel;
35453 panel.setActiveState(true);
35454 if(this.panelSize){
35455 panel.setSize(this.panelSize.width, this.panelSize.height);
35458 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35460 this.updateTitle(panel.getTitle());
35462 this.fireEvent("invalidated", this);
35464 this.fireEvent("panelactivated", this, panel);
35468 * Shows the specified panel.
35469 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35470 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35472 showPanel : function(panel)
35474 panel = this.getPanel(panel);
35477 var tab = this.tabs.getTab(panel.getEl().id);
35478 if(tab.isHidden()){
35479 this.tabs.unhideTab(tab.id);
35483 this.setActivePanel(panel);
35490 * Get the active panel for this region.
35491 * @return {Roo.ContentPanel} The active panel or null
35493 getActivePanel : function(){
35494 return this.activePanel;
35497 validateVisibility : function(){
35498 if(this.panels.getCount() < 1){
35499 this.updateTitle(" ");
35500 this.closeBtn.hide();
35503 if(!this.isVisible()){
35510 * Adds the passed ContentPanel(s) to this region.
35511 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35512 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35514 add : function(panel)
35516 if(arguments.length > 1){
35517 for(var i = 0, len = arguments.length; i < len; i++) {
35518 this.add(arguments[i]);
35523 // if we have not been rendered yet, then we can not really do much of this..
35524 if (!this.bodyEl) {
35525 this.unrendered_panels.push(panel);
35532 if(this.hasPanel(panel)){
35533 this.showPanel(panel);
35536 panel.setRegion(this);
35537 this.panels.add(panel);
35538 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35539 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35540 // and hide them... ???
35541 this.bodyEl.dom.appendChild(panel.getEl().dom);
35542 if(panel.background !== true){
35543 this.setActivePanel(panel);
35545 this.fireEvent("paneladded", this, panel);
35552 this.initPanelAsTab(panel);
35556 if(panel.background !== true){
35557 this.tabs.activate(panel.getEl().id);
35559 this.fireEvent("paneladded", this, panel);
35564 * Hides the tab for the specified panel.
35565 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35567 hidePanel : function(panel){
35568 if(this.tabs && (panel = this.getPanel(panel))){
35569 this.tabs.hideTab(panel.getEl().id);
35574 * Unhides the tab for a previously hidden panel.
35575 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35577 unhidePanel : function(panel){
35578 if(this.tabs && (panel = this.getPanel(panel))){
35579 this.tabs.unhideTab(panel.getEl().id);
35583 clearPanels : function(){
35584 while(this.panels.getCount() > 0){
35585 this.remove(this.panels.first());
35590 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35591 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35592 * @param {Boolean} preservePanel Overrides the config preservePanel option
35593 * @return {Roo.ContentPanel} The panel that was removed
35595 remove : function(panel, preservePanel)
35597 panel = this.getPanel(panel);
35602 this.fireEvent("beforeremove", this, panel, e);
35603 if(e.cancel === true){
35606 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35607 var panelId = panel.getId();
35608 this.panels.removeKey(panelId);
35610 document.body.appendChild(panel.getEl().dom);
35613 this.tabs.removeTab(panel.getEl().id);
35614 }else if (!preservePanel){
35615 this.bodyEl.dom.removeChild(panel.getEl().dom);
35617 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35618 var p = this.panels.first();
35619 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35620 tempEl.appendChild(p.getEl().dom);
35621 this.bodyEl.update("");
35622 this.bodyEl.dom.appendChild(p.getEl().dom);
35624 this.updateTitle(p.getTitle());
35626 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35627 this.setActivePanel(p);
35629 panel.setRegion(null);
35630 if(this.activePanel == panel){
35631 this.activePanel = null;
35633 if(this.config.autoDestroy !== false && preservePanel !== true){
35634 try{panel.destroy();}catch(e){}
35636 this.fireEvent("panelremoved", this, panel);
35641 * Returns the TabPanel component used by this region
35642 * @return {Roo.TabPanel}
35644 getTabs : function(){
35648 createTool : function(parentEl, className){
35649 var btn = Roo.DomHelper.append(parentEl, {
35651 cls: "x-layout-tools-button",
35654 cls: "roo-layout-tools-button-inner " + className,
35658 btn.addClassOnOver("roo-layout-tools-button-over");
35663 * Ext JS Library 1.1.1
35664 * Copyright(c) 2006-2007, Ext JS, LLC.
35666 * Originally Released Under LGPL - original licence link has changed is not relivant.
35669 * <script type="text/javascript">
35675 * @class Roo.SplitLayoutRegion
35676 * @extends Roo.LayoutRegion
35677 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35679 Roo.bootstrap.layout.Split = function(config){
35680 this.cursor = config.cursor;
35681 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35684 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35686 splitTip : "Drag to resize.",
35687 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35688 useSplitTips : false,
35690 applyConfig : function(config){
35691 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35694 onRender : function(ctr,pos) {
35696 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35697 if(!this.config.split){
35702 var splitEl = Roo.DomHelper.append(ctr.dom, {
35704 id: this.el.id + "-split",
35705 cls: "roo-layout-split roo-layout-split-"+this.position,
35708 /** The SplitBar for this region
35709 * @type Roo.SplitBar */
35710 // does not exist yet...
35711 Roo.log([this.position, this.orientation]);
35713 this.split = new Roo.bootstrap.SplitBar({
35714 dragElement : splitEl,
35715 resizingElement: this.el,
35716 orientation : this.orientation
35719 this.split.on("moved", this.onSplitMove, this);
35720 this.split.useShim = this.config.useShim === true;
35721 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35722 if(this.useSplitTips){
35723 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35725 //if(config.collapsible){
35726 // this.split.el.on("dblclick", this.collapse, this);
35729 if(typeof this.config.minSize != "undefined"){
35730 this.split.minSize = this.config.minSize;
35732 if(typeof this.config.maxSize != "undefined"){
35733 this.split.maxSize = this.config.maxSize;
35735 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35736 this.hideSplitter();
35741 getHMaxSize : function(){
35742 var cmax = this.config.maxSize || 10000;
35743 var center = this.mgr.getRegion("center");
35744 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35747 getVMaxSize : function(){
35748 var cmax = this.config.maxSize || 10000;
35749 var center = this.mgr.getRegion("center");
35750 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35753 onSplitMove : function(split, newSize){
35754 this.fireEvent("resized", this, newSize);
35758 * Returns the {@link Roo.SplitBar} for this region.
35759 * @return {Roo.SplitBar}
35761 getSplitBar : function(){
35766 this.hideSplitter();
35767 Roo.bootstrap.layout.Split.superclass.hide.call(this);
35770 hideSplitter : function(){
35772 this.split.el.setLocation(-2000,-2000);
35773 this.split.el.hide();
35779 this.split.el.show();
35781 Roo.bootstrap.layout.Split.superclass.show.call(this);
35784 beforeSlide: function(){
35785 if(Roo.isGecko){// firefox overflow auto bug workaround
35786 this.bodyEl.clip();
35788 this.tabs.bodyEl.clip();
35790 if(this.activePanel){
35791 this.activePanel.getEl().clip();
35793 if(this.activePanel.beforeSlide){
35794 this.activePanel.beforeSlide();
35800 afterSlide : function(){
35801 if(Roo.isGecko){// firefox overflow auto bug workaround
35802 this.bodyEl.unclip();
35804 this.tabs.bodyEl.unclip();
35806 if(this.activePanel){
35807 this.activePanel.getEl().unclip();
35808 if(this.activePanel.afterSlide){
35809 this.activePanel.afterSlide();
35815 initAutoHide : function(){
35816 if(this.autoHide !== false){
35817 if(!this.autoHideHd){
35818 var st = new Roo.util.DelayedTask(this.slideIn, this);
35819 this.autoHideHd = {
35820 "mouseout": function(e){
35821 if(!e.within(this.el, true)){
35825 "mouseover" : function(e){
35831 this.el.on(this.autoHideHd);
35835 clearAutoHide : function(){
35836 if(this.autoHide !== false){
35837 this.el.un("mouseout", this.autoHideHd.mouseout);
35838 this.el.un("mouseover", this.autoHideHd.mouseover);
35842 clearMonitor : function(){
35843 Roo.get(document).un("click", this.slideInIf, this);
35846 // these names are backwards but not changed for compat
35847 slideOut : function(){
35848 if(this.isSlid || this.el.hasActiveFx()){
35851 this.isSlid = true;
35852 if(this.collapseBtn){
35853 this.collapseBtn.hide();
35855 this.closeBtnState = this.closeBtn.getStyle('display');
35856 this.closeBtn.hide();
35858 this.stickBtn.show();
35861 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35862 this.beforeSlide();
35863 this.el.setStyle("z-index", 10001);
35864 this.el.slideIn(this.getSlideAnchor(), {
35865 callback: function(){
35867 this.initAutoHide();
35868 Roo.get(document).on("click", this.slideInIf, this);
35869 this.fireEvent("slideshow", this);
35876 afterSlideIn : function(){
35877 this.clearAutoHide();
35878 this.isSlid = false;
35879 this.clearMonitor();
35880 this.el.setStyle("z-index", "");
35881 if(this.collapseBtn){
35882 this.collapseBtn.show();
35884 this.closeBtn.setStyle('display', this.closeBtnState);
35886 this.stickBtn.hide();
35888 this.fireEvent("slidehide", this);
35891 slideIn : function(cb){
35892 if(!this.isSlid || this.el.hasActiveFx()){
35896 this.isSlid = false;
35897 this.beforeSlide();
35898 this.el.slideOut(this.getSlideAnchor(), {
35899 callback: function(){
35900 this.el.setLeftTop(-10000, -10000);
35902 this.afterSlideIn();
35910 slideInIf : function(e){
35911 if(!e.within(this.el)){
35916 animateCollapse : function(){
35917 this.beforeSlide();
35918 this.el.setStyle("z-index", 20000);
35919 var anchor = this.getSlideAnchor();
35920 this.el.slideOut(anchor, {
35921 callback : function(){
35922 this.el.setStyle("z-index", "");
35923 this.collapsedEl.slideIn(anchor, {duration:.3});
35925 this.el.setLocation(-10000,-10000);
35927 this.fireEvent("collapsed", this);
35934 animateExpand : function(){
35935 this.beforeSlide();
35936 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35937 this.el.setStyle("z-index", 20000);
35938 this.collapsedEl.hide({
35941 this.el.slideIn(this.getSlideAnchor(), {
35942 callback : function(){
35943 this.el.setStyle("z-index", "");
35946 this.split.el.show();
35948 this.fireEvent("invalidated", this);
35949 this.fireEvent("expanded", this);
35977 getAnchor : function(){
35978 return this.anchors[this.position];
35981 getCollapseAnchor : function(){
35982 return this.canchors[this.position];
35985 getSlideAnchor : function(){
35986 return this.sanchors[this.position];
35989 getAlignAdj : function(){
35990 var cm = this.cmargins;
35991 switch(this.position){
36007 getExpandAdj : function(){
36008 var c = this.collapsedEl, cm = this.cmargins;
36009 switch(this.position){
36011 return [-(cm.right+c.getWidth()+cm.left), 0];
36014 return [cm.right+c.getWidth()+cm.left, 0];
36017 return [0, -(cm.top+cm.bottom+c.getHeight())];
36020 return [0, cm.top+cm.bottom+c.getHeight()];
36026 * Ext JS Library 1.1.1
36027 * Copyright(c) 2006-2007, Ext JS, LLC.
36029 * Originally Released Under LGPL - original licence link has changed is not relivant.
36032 * <script type="text/javascript">
36035 * These classes are private internal classes
36037 Roo.bootstrap.layout.Center = function(config){
36038 config.region = "center";
36039 Roo.bootstrap.layout.Region.call(this, config);
36040 this.visible = true;
36041 this.minWidth = config.minWidth || 20;
36042 this.minHeight = config.minHeight || 20;
36045 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36047 // center panel can't be hidden
36051 // center panel can't be hidden
36054 getMinWidth: function(){
36055 return this.minWidth;
36058 getMinHeight: function(){
36059 return this.minHeight;
36072 Roo.bootstrap.layout.North = function(config)
36074 config.region = 'north';
36075 config.cursor = 'n-resize';
36077 Roo.bootstrap.layout.Split.call(this, config);
36081 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36082 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36083 this.split.el.addClass("roo-layout-split-v");
36085 var size = config.initialSize || config.height;
36086 if(typeof size != "undefined"){
36087 this.el.setHeight(size);
36090 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36092 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36096 getBox : function(){
36097 if(this.collapsed){
36098 return this.collapsedEl.getBox();
36100 var box = this.el.getBox();
36102 box.height += this.split.el.getHeight();
36107 updateBox : function(box){
36108 if(this.split && !this.collapsed){
36109 box.height -= this.split.el.getHeight();
36110 this.split.el.setLeft(box.x);
36111 this.split.el.setTop(box.y+box.height);
36112 this.split.el.setWidth(box.width);
36114 if(this.collapsed){
36115 this.updateBody(box.width, null);
36117 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36125 Roo.bootstrap.layout.South = function(config){
36126 config.region = 'south';
36127 config.cursor = 's-resize';
36128 Roo.bootstrap.layout.Split.call(this, config);
36130 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36131 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36132 this.split.el.addClass("roo-layout-split-v");
36134 var size = config.initialSize || config.height;
36135 if(typeof size != "undefined"){
36136 this.el.setHeight(size);
36140 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36141 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36142 getBox : function(){
36143 if(this.collapsed){
36144 return this.collapsedEl.getBox();
36146 var box = this.el.getBox();
36148 var sh = this.split.el.getHeight();
36155 updateBox : function(box){
36156 if(this.split && !this.collapsed){
36157 var sh = this.split.el.getHeight();
36160 this.split.el.setLeft(box.x);
36161 this.split.el.setTop(box.y-sh);
36162 this.split.el.setWidth(box.width);
36164 if(this.collapsed){
36165 this.updateBody(box.width, null);
36167 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36171 Roo.bootstrap.layout.East = function(config){
36172 config.region = "east";
36173 config.cursor = "e-resize";
36174 Roo.bootstrap.layout.Split.call(this, config);
36176 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36177 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36178 this.split.el.addClass("roo-layout-split-h");
36180 var size = config.initialSize || config.width;
36181 if(typeof size != "undefined"){
36182 this.el.setWidth(size);
36185 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36186 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36187 getBox : function(){
36188 if(this.collapsed){
36189 return this.collapsedEl.getBox();
36191 var box = this.el.getBox();
36193 var sw = this.split.el.getWidth();
36200 updateBox : function(box){
36201 if(this.split && !this.collapsed){
36202 var sw = this.split.el.getWidth();
36204 this.split.el.setLeft(box.x);
36205 this.split.el.setTop(box.y);
36206 this.split.el.setHeight(box.height);
36209 if(this.collapsed){
36210 this.updateBody(null, box.height);
36212 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36216 Roo.bootstrap.layout.West = function(config){
36217 config.region = "west";
36218 config.cursor = "w-resize";
36220 Roo.bootstrap.layout.Split.call(this, config);
36222 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36223 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36224 this.split.el.addClass("roo-layout-split-h");
36228 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36229 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36231 onRender: function(ctr, pos)
36233 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36234 var size = this.config.initialSize || this.config.width;
36235 if(typeof size != "undefined"){
36236 this.el.setWidth(size);
36240 getBox : function(){
36241 if(this.collapsed){
36242 return this.collapsedEl.getBox();
36244 var box = this.el.getBox();
36246 box.width += this.split.el.getWidth();
36251 updateBox : function(box){
36252 if(this.split && !this.collapsed){
36253 var sw = this.split.el.getWidth();
36255 this.split.el.setLeft(box.x+box.width);
36256 this.split.el.setTop(box.y);
36257 this.split.el.setHeight(box.height);
36259 if(this.collapsed){
36260 this.updateBody(null, box.height);
36262 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36265 Roo.namespace("Roo.bootstrap.panel");/*
36267 * Ext JS Library 1.1.1
36268 * Copyright(c) 2006-2007, Ext JS, LLC.
36270 * Originally Released Under LGPL - original licence link has changed is not relivant.
36273 * <script type="text/javascript">
36276 * @class Roo.ContentPanel
36277 * @extends Roo.util.Observable
36278 * A basic ContentPanel element.
36279 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36280 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36281 * @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
36282 * @cfg {Boolean} closable True if the panel can be closed/removed
36283 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36284 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36285 * @cfg {Toolbar} toolbar A toolbar for this panel
36286 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36287 * @cfg {String} title The title for this panel
36288 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36289 * @cfg {String} url Calls {@link #setUrl} with this value
36290 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36291 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36292 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36293 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36294 * @cfg {Boolean} badges render the badges
36297 * Create a new ContentPanel.
36298 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36299 * @param {String/Object} config A string to set only the title or a config object
36300 * @param {String} content (optional) Set the HTML content for this panel
36301 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36303 Roo.bootstrap.panel.Content = function( config){
36305 this.tpl = config.tpl || false;
36307 var el = config.el;
36308 var content = config.content;
36310 if(config.autoCreate){ // xtype is available if this is called from factory
36313 this.el = Roo.get(el);
36314 if(!this.el && config && config.autoCreate){
36315 if(typeof config.autoCreate == "object"){
36316 if(!config.autoCreate.id){
36317 config.autoCreate.id = config.id||el;
36319 this.el = Roo.DomHelper.append(document.body,
36320 config.autoCreate, true);
36322 var elcfg = { tag: "div",
36323 cls: "roo-layout-inactive-content",
36327 elcfg.html = config.html;
36331 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36334 this.closable = false;
36335 this.loaded = false;
36336 this.active = false;
36339 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36341 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36343 this.wrapEl = this.el; //this.el.wrap();
36345 if (config.toolbar.items) {
36346 ti = config.toolbar.items ;
36347 delete config.toolbar.items ;
36351 this.toolbar.render(this.wrapEl, 'before');
36352 for(var i =0;i < ti.length;i++) {
36353 // Roo.log(['add child', items[i]]);
36354 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36356 this.toolbar.items = nitems;
36357 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36358 delete config.toolbar;
36362 // xtype created footer. - not sure if will work as we normally have to render first..
36363 if (this.footer && !this.footer.el && this.footer.xtype) {
36364 if (!this.wrapEl) {
36365 this.wrapEl = this.el.wrap();
36368 this.footer.container = this.wrapEl.createChild();
36370 this.footer = Roo.factory(this.footer, Roo);
36375 if(typeof config == "string"){
36376 this.title = config;
36378 Roo.apply(this, config);
36382 this.resizeEl = Roo.get(this.resizeEl, true);
36384 this.resizeEl = this.el;
36386 // handle view.xtype
36394 * Fires when this panel is activated.
36395 * @param {Roo.ContentPanel} this
36399 * @event deactivate
36400 * Fires when this panel is activated.
36401 * @param {Roo.ContentPanel} this
36403 "deactivate" : true,
36407 * Fires when this panel is resized if fitToFrame is true.
36408 * @param {Roo.ContentPanel} this
36409 * @param {Number} width The width after any component adjustments
36410 * @param {Number} height The height after any component adjustments
36416 * Fires when this tab is created
36417 * @param {Roo.ContentPanel} this
36428 if(this.autoScroll){
36429 this.resizeEl.setStyle("overflow", "auto");
36431 // fix randome scrolling
36432 //this.el.on('scroll', function() {
36433 // Roo.log('fix random scolling');
36434 // this.scrollTo('top',0);
36437 content = content || this.content;
36439 this.setContent(content);
36441 if(config && config.url){
36442 this.setUrl(this.url, this.params, this.loadOnce);
36447 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36449 if (this.view && typeof(this.view.xtype) != 'undefined') {
36450 this.view.el = this.el.appendChild(document.createElement("div"));
36451 this.view = Roo.factory(this.view);
36452 this.view.render && this.view.render(false, '');
36456 this.fireEvent('render', this);
36459 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36463 setRegion : function(region){
36464 this.region = region;
36465 this.setActiveClass(region && !this.background);
36469 setActiveClass: function(state)
36472 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36473 this.el.setStyle('position','relative');
36475 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36476 this.el.setStyle('position', 'absolute');
36481 * Returns the toolbar for this Panel if one was configured.
36482 * @return {Roo.Toolbar}
36484 getToolbar : function(){
36485 return this.toolbar;
36488 setActiveState : function(active)
36490 this.active = active;
36491 this.setActiveClass(active);
36493 this.fireEvent("deactivate", this);
36495 this.fireEvent("activate", this);
36499 * Updates this panel's element
36500 * @param {String} content The new content
36501 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36503 setContent : function(content, loadScripts){
36504 this.el.update(content, loadScripts);
36507 ignoreResize : function(w, h){
36508 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36511 this.lastSize = {width: w, height: h};
36516 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36517 * @return {Roo.UpdateManager} The UpdateManager
36519 getUpdateManager : function(){
36520 return this.el.getUpdateManager();
36523 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36524 * @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:
36527 url: "your-url.php",
36528 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36529 callback: yourFunction,
36530 scope: yourObject, //(optional scope)
36533 text: "Loading...",
36538 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36539 * 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.
36540 * @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}
36541 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36542 * @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.
36543 * @return {Roo.ContentPanel} this
36546 var um = this.el.getUpdateManager();
36547 um.update.apply(um, arguments);
36553 * 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.
36554 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36555 * @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)
36556 * @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)
36557 * @return {Roo.UpdateManager} The UpdateManager
36559 setUrl : function(url, params, loadOnce){
36560 if(this.refreshDelegate){
36561 this.removeListener("activate", this.refreshDelegate);
36563 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36564 this.on("activate", this.refreshDelegate);
36565 return this.el.getUpdateManager();
36568 _handleRefresh : function(url, params, loadOnce){
36569 if(!loadOnce || !this.loaded){
36570 var updater = this.el.getUpdateManager();
36571 updater.update(url, params, this._setLoaded.createDelegate(this));
36575 _setLoaded : function(){
36576 this.loaded = true;
36580 * Returns this panel's id
36583 getId : function(){
36588 * Returns this panel's element - used by regiosn to add.
36589 * @return {Roo.Element}
36591 getEl : function(){
36592 return this.wrapEl || this.el;
36597 adjustForComponents : function(width, height)
36599 //Roo.log('adjustForComponents ');
36600 if(this.resizeEl != this.el){
36601 width -= this.el.getFrameWidth('lr');
36602 height -= this.el.getFrameWidth('tb');
36605 var te = this.toolbar.getEl();
36606 te.setWidth(width);
36607 height -= te.getHeight();
36610 var te = this.footer.getEl();
36611 te.setWidth(width);
36612 height -= te.getHeight();
36616 if(this.adjustments){
36617 width += this.adjustments[0];
36618 height += this.adjustments[1];
36620 return {"width": width, "height": height};
36623 setSize : function(width, height){
36624 if(this.fitToFrame && !this.ignoreResize(width, height)){
36625 if(this.fitContainer && this.resizeEl != this.el){
36626 this.el.setSize(width, height);
36628 var size = this.adjustForComponents(width, height);
36629 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36630 this.fireEvent('resize', this, size.width, size.height);
36635 * Returns this panel's title
36638 getTitle : function(){
36640 if (typeof(this.title) != 'object') {
36645 for (var k in this.title) {
36646 if (!this.title.hasOwnProperty(k)) {
36650 if (k.indexOf('-') >= 0) {
36651 var s = k.split('-');
36652 for (var i = 0; i<s.length; i++) {
36653 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36656 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36663 * Set this panel's title
36664 * @param {String} title
36666 setTitle : function(title){
36667 this.title = title;
36669 this.region.updatePanelTitle(this, title);
36674 * Returns true is this panel was configured to be closable
36675 * @return {Boolean}
36677 isClosable : function(){
36678 return this.closable;
36681 beforeSlide : function(){
36683 this.resizeEl.clip();
36686 afterSlide : function(){
36688 this.resizeEl.unclip();
36692 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36693 * Will fail silently if the {@link #setUrl} method has not been called.
36694 * This does not activate the panel, just updates its content.
36696 refresh : function(){
36697 if(this.refreshDelegate){
36698 this.loaded = false;
36699 this.refreshDelegate();
36704 * Destroys this panel
36706 destroy : function(){
36707 this.el.removeAllListeners();
36708 var tempEl = document.createElement("span");
36709 tempEl.appendChild(this.el.dom);
36710 tempEl.innerHTML = "";
36716 * form - if the content panel contains a form - this is a reference to it.
36717 * @type {Roo.form.Form}
36721 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36722 * This contains a reference to it.
36728 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36738 * @param {Object} cfg Xtype definition of item to add.
36742 getChildContainer: function () {
36743 return this.getEl();
36748 var ret = new Roo.factory(cfg);
36753 if (cfg.xtype.match(/^Form$/)) {
36756 //if (this.footer) {
36757 // el = this.footer.container.insertSibling(false, 'before');
36759 el = this.el.createChild();
36762 this.form = new Roo.form.Form(cfg);
36765 if ( this.form.allItems.length) {
36766 this.form.render(el.dom);
36770 // should only have one of theses..
36771 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36772 // views.. should not be just added - used named prop 'view''
36774 cfg.el = this.el.appendChild(document.createElement("div"));
36777 var ret = new Roo.factory(cfg);
36779 ret.render && ret.render(false, ''); // render blank..
36789 * @class Roo.bootstrap.panel.Grid
36790 * @extends Roo.bootstrap.panel.Content
36792 * Create a new GridPanel.
36793 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36794 * @param {Object} config A the config object
36800 Roo.bootstrap.panel.Grid = function(config)
36804 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36805 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36807 config.el = this.wrapper;
36808 //this.el = this.wrapper;
36810 if (config.container) {
36811 // ctor'ed from a Border/panel.grid
36814 this.wrapper.setStyle("overflow", "hidden");
36815 this.wrapper.addClass('roo-grid-container');
36820 if(config.toolbar){
36821 var tool_el = this.wrapper.createChild();
36822 this.toolbar = Roo.factory(config.toolbar);
36824 if (config.toolbar.items) {
36825 ti = config.toolbar.items ;
36826 delete config.toolbar.items ;
36830 this.toolbar.render(tool_el);
36831 for(var i =0;i < ti.length;i++) {
36832 // Roo.log(['add child', items[i]]);
36833 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36835 this.toolbar.items = nitems;
36837 delete config.toolbar;
36840 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36841 config.grid.scrollBody = true;;
36842 config.grid.monitorWindowResize = false; // turn off autosizing
36843 config.grid.autoHeight = false;
36844 config.grid.autoWidth = false;
36846 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36848 if (config.background) {
36849 // render grid on panel activation (if panel background)
36850 this.on('activate', function(gp) {
36851 if (!gp.grid.rendered) {
36852 gp.grid.render(this.wrapper);
36853 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36858 this.grid.render(this.wrapper);
36859 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36862 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36863 // ??? needed ??? config.el = this.wrapper;
36868 // xtype created footer. - not sure if will work as we normally have to render first..
36869 if (this.footer && !this.footer.el && this.footer.xtype) {
36871 var ctr = this.grid.getView().getFooterPanel(true);
36872 this.footer.dataSource = this.grid.dataSource;
36873 this.footer = Roo.factory(this.footer, Roo);
36874 this.footer.render(ctr);
36884 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36885 getId : function(){
36886 return this.grid.id;
36890 * Returns the grid for this panel
36891 * @return {Roo.bootstrap.Table}
36893 getGrid : function(){
36897 setSize : function(width, height){
36898 if(!this.ignoreResize(width, height)){
36899 var grid = this.grid;
36900 var size = this.adjustForComponents(width, height);
36901 var gridel = grid.getGridEl();
36902 gridel.setSize(size.width, size.height);
36904 var thd = grid.getGridEl().select('thead',true).first();
36905 var tbd = grid.getGridEl().select('tbody', true).first();
36907 tbd.setSize(width, height - thd.getHeight());
36916 beforeSlide : function(){
36917 this.grid.getView().scroller.clip();
36920 afterSlide : function(){
36921 this.grid.getView().scroller.unclip();
36924 destroy : function(){
36925 this.grid.destroy();
36927 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
36932 * @class Roo.bootstrap.panel.Nest
36933 * @extends Roo.bootstrap.panel.Content
36935 * Create a new Panel, that can contain a layout.Border.
36938 * @param {Roo.BorderLayout} layout The layout for this panel
36939 * @param {String/Object} config A string to set only the title or a config object
36941 Roo.bootstrap.panel.Nest = function(config)
36943 // construct with only one argument..
36944 /* FIXME - implement nicer consturctors
36945 if (layout.layout) {
36947 layout = config.layout;
36948 delete config.layout;
36950 if (layout.xtype && !layout.getEl) {
36951 // then layout needs constructing..
36952 layout = Roo.factory(layout, Roo);
36956 config.el = config.layout.getEl();
36958 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36960 config.layout.monitorWindowResize = false; // turn off autosizing
36961 this.layout = config.layout;
36962 this.layout.getEl().addClass("roo-layout-nested-layout");
36969 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36971 setSize : function(width, height){
36972 if(!this.ignoreResize(width, height)){
36973 var size = this.adjustForComponents(width, height);
36974 var el = this.layout.getEl();
36975 if (size.height < 1) {
36976 el.setWidth(size.width);
36978 el.setSize(size.width, size.height);
36980 var touch = el.dom.offsetWidth;
36981 this.layout.layout();
36982 // ie requires a double layout on the first pass
36983 if(Roo.isIE && !this.initialized){
36984 this.initialized = true;
36985 this.layout.layout();
36990 // activate all subpanels if not currently active..
36992 setActiveState : function(active){
36993 this.active = active;
36994 this.setActiveClass(active);
36997 this.fireEvent("deactivate", this);
37001 this.fireEvent("activate", this);
37002 // not sure if this should happen before or after..
37003 if (!this.layout) {
37004 return; // should not happen..
37007 for (var r in this.layout.regions) {
37008 reg = this.layout.getRegion(r);
37009 if (reg.getActivePanel()) {
37010 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37011 reg.setActivePanel(reg.getActivePanel());
37014 if (!reg.panels.length) {
37017 reg.showPanel(reg.getPanel(0));
37026 * Returns the nested BorderLayout for this panel
37027 * @return {Roo.BorderLayout}
37029 getLayout : function(){
37030 return this.layout;
37034 * Adds a xtype elements to the layout of the nested panel
37038 xtype : 'ContentPanel',
37045 xtype : 'NestedLayoutPanel',
37051 items : [ ... list of content panels or nested layout panels.. ]
37055 * @param {Object} cfg Xtype definition of item to add.
37057 addxtype : function(cfg) {
37058 return this.layout.addxtype(cfg);
37063 * Ext JS Library 1.1.1
37064 * Copyright(c) 2006-2007, Ext JS, LLC.
37066 * Originally Released Under LGPL - original licence link has changed is not relivant.
37069 * <script type="text/javascript">
37072 * @class Roo.TabPanel
37073 * @extends Roo.util.Observable
37074 * A lightweight tab container.
37078 // basic tabs 1, built from existing content
37079 var tabs = new Roo.TabPanel("tabs1");
37080 tabs.addTab("script", "View Script");
37081 tabs.addTab("markup", "View Markup");
37082 tabs.activate("script");
37084 // more advanced tabs, built from javascript
37085 var jtabs = new Roo.TabPanel("jtabs");
37086 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37088 // set up the UpdateManager
37089 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37090 var updater = tab2.getUpdateManager();
37091 updater.setDefaultUrl("ajax1.htm");
37092 tab2.on('activate', updater.refresh, updater, true);
37094 // Use setUrl for Ajax loading
37095 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37096 tab3.setUrl("ajax2.htm", null, true);
37099 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37102 jtabs.activate("jtabs-1");
37105 * Create a new TabPanel.
37106 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37107 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37109 Roo.bootstrap.panel.Tabs = function(config){
37111 * The container element for this TabPanel.
37112 * @type Roo.Element
37114 this.el = Roo.get(config.el);
37117 if(typeof config == "boolean"){
37118 this.tabPosition = config ? "bottom" : "top";
37120 Roo.apply(this, config);
37124 if(this.tabPosition == "bottom"){
37125 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37126 this.el.addClass("roo-tabs-bottom");
37128 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37129 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37130 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37132 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37134 if(this.tabPosition != "bottom"){
37135 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37136 * @type Roo.Element
37138 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37139 this.el.addClass("roo-tabs-top");
37143 this.bodyEl.setStyle("position", "relative");
37145 this.active = null;
37146 this.activateDelegate = this.activate.createDelegate(this);
37151 * Fires when the active tab changes
37152 * @param {Roo.TabPanel} this
37153 * @param {Roo.TabPanelItem} activePanel The new active tab
37157 * @event beforetabchange
37158 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37159 * @param {Roo.TabPanel} this
37160 * @param {Object} e Set cancel to true on this object to cancel the tab change
37161 * @param {Roo.TabPanelItem} tab The tab being changed to
37163 "beforetabchange" : true
37166 Roo.EventManager.onWindowResize(this.onResize, this);
37167 this.cpad = this.el.getPadding("lr");
37168 this.hiddenCount = 0;
37171 // toolbar on the tabbar support...
37172 if (this.toolbar) {
37173 alert("no toolbar support yet");
37174 this.toolbar = false;
37176 var tcfg = this.toolbar;
37177 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37178 this.toolbar = new Roo.Toolbar(tcfg);
37179 if (Roo.isSafari) {
37180 var tbl = tcfg.container.child('table', true);
37181 tbl.setAttribute('width', '100%');
37189 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37192 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37194 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37196 tabPosition : "top",
37198 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37200 currentTabWidth : 0,
37202 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37206 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37210 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37212 preferredTabWidth : 175,
37214 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37216 resizeTabs : false,
37218 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37220 monitorResize : true,
37222 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37227 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37228 * @param {String} id The id of the div to use <b>or create</b>
37229 * @param {String} text The text for the tab
37230 * @param {String} content (optional) Content to put in the TabPanelItem body
37231 * @param {Boolean} closable (optional) True to create a close icon on the tab
37232 * @return {Roo.TabPanelItem} The created TabPanelItem
37234 addTab : function(id, text, content, closable, tpl)
37236 var item = new Roo.bootstrap.panel.TabItem({
37240 closable : closable,
37243 this.addTabItem(item);
37245 item.setContent(content);
37251 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37252 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37253 * @return {Roo.TabPanelItem}
37255 getTab : function(id){
37256 return this.items[id];
37260 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37261 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37263 hideTab : function(id){
37264 var t = this.items[id];
37267 this.hiddenCount++;
37268 this.autoSizeTabs();
37273 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37274 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37276 unhideTab : function(id){
37277 var t = this.items[id];
37279 t.setHidden(false);
37280 this.hiddenCount--;
37281 this.autoSizeTabs();
37286 * Adds an existing {@link Roo.TabPanelItem}.
37287 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37289 addTabItem : function(item){
37290 this.items[item.id] = item;
37291 this.items.push(item);
37292 // if(this.resizeTabs){
37293 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37294 // this.autoSizeTabs();
37296 // item.autoSize();
37301 * Removes a {@link Roo.TabPanelItem}.
37302 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37304 removeTab : function(id){
37305 var items = this.items;
37306 var tab = items[id];
37307 if(!tab) { return; }
37308 var index = items.indexOf(tab);
37309 if(this.active == tab && items.length > 1){
37310 var newTab = this.getNextAvailable(index);
37315 this.stripEl.dom.removeChild(tab.pnode.dom);
37316 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37317 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37319 items.splice(index, 1);
37320 delete this.items[tab.id];
37321 tab.fireEvent("close", tab);
37322 tab.purgeListeners();
37323 this.autoSizeTabs();
37326 getNextAvailable : function(start){
37327 var items = this.items;
37329 // look for a next tab that will slide over to
37330 // replace the one being removed
37331 while(index < items.length){
37332 var item = items[++index];
37333 if(item && !item.isHidden()){
37337 // if one isn't found select the previous tab (on the left)
37340 var item = items[--index];
37341 if(item && !item.isHidden()){
37349 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37350 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37352 disableTab : function(id){
37353 var tab = this.items[id];
37354 if(tab && this.active != tab){
37360 * Enables a {@link Roo.TabPanelItem} that is disabled.
37361 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37363 enableTab : function(id){
37364 var tab = this.items[id];
37369 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37370 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37371 * @return {Roo.TabPanelItem} The TabPanelItem.
37373 activate : function(id){
37374 var tab = this.items[id];
37378 if(tab == this.active || tab.disabled){
37382 this.fireEvent("beforetabchange", this, e, tab);
37383 if(e.cancel !== true && !tab.disabled){
37385 this.active.hide();
37387 this.active = this.items[id];
37388 this.active.show();
37389 this.fireEvent("tabchange", this, this.active);
37395 * Gets the active {@link Roo.TabPanelItem}.
37396 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37398 getActiveTab : function(){
37399 return this.active;
37403 * Updates the tab body element to fit the height of the container element
37404 * for overflow scrolling
37405 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37407 syncHeight : function(targetHeight){
37408 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37409 var bm = this.bodyEl.getMargins();
37410 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37411 this.bodyEl.setHeight(newHeight);
37415 onResize : function(){
37416 if(this.monitorResize){
37417 this.autoSizeTabs();
37422 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37424 beginUpdate : function(){
37425 this.updating = true;
37429 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37431 endUpdate : function(){
37432 this.updating = false;
37433 this.autoSizeTabs();
37437 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37439 autoSizeTabs : function(){
37440 var count = this.items.length;
37441 var vcount = count - this.hiddenCount;
37442 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37445 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37446 var availWidth = Math.floor(w / vcount);
37447 var b = this.stripBody;
37448 if(b.getWidth() > w){
37449 var tabs = this.items;
37450 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37451 if(availWidth < this.minTabWidth){
37452 /*if(!this.sleft){ // incomplete scrolling code
37453 this.createScrollButtons();
37456 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37459 if(this.currentTabWidth < this.preferredTabWidth){
37460 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37466 * Returns the number of tabs in this TabPanel.
37469 getCount : function(){
37470 return this.items.length;
37474 * Resizes all the tabs to the passed width
37475 * @param {Number} The new width
37477 setTabWidth : function(width){
37478 this.currentTabWidth = width;
37479 for(var i = 0, len = this.items.length; i < len; i++) {
37480 if(!this.items[i].isHidden()) {
37481 this.items[i].setWidth(width);
37487 * Destroys this TabPanel
37488 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37490 destroy : function(removeEl){
37491 Roo.EventManager.removeResizeListener(this.onResize, this);
37492 for(var i = 0, len = this.items.length; i < len; i++){
37493 this.items[i].purgeListeners();
37495 if(removeEl === true){
37496 this.el.update("");
37501 createStrip : function(container)
37503 var strip = document.createElement("nav");
37504 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37505 container.appendChild(strip);
37509 createStripList : function(strip)
37511 // div wrapper for retard IE
37512 // returns the "tr" element.
37513 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37514 //'<div class="x-tabs-strip-wrap">'+
37515 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37516 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37517 return strip.firstChild; //.firstChild.firstChild.firstChild;
37519 createBody : function(container)
37521 var body = document.createElement("div");
37522 Roo.id(body, "tab-body");
37523 //Roo.fly(body).addClass("x-tabs-body");
37524 Roo.fly(body).addClass("tab-content");
37525 container.appendChild(body);
37528 createItemBody :function(bodyEl, id){
37529 var body = Roo.getDom(id);
37531 body = document.createElement("div");
37534 //Roo.fly(body).addClass("x-tabs-item-body");
37535 Roo.fly(body).addClass("tab-pane");
37536 bodyEl.insertBefore(body, bodyEl.firstChild);
37540 createStripElements : function(stripEl, text, closable, tpl)
37542 var td = document.createElement("li"); // was td..
37545 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37548 stripEl.appendChild(td);
37550 td.className = "x-tabs-closable";
37551 if(!this.closeTpl){
37552 this.closeTpl = new Roo.Template(
37553 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37554 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37555 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
37558 var el = this.closeTpl.overwrite(td, {"text": text});
37559 var close = el.getElementsByTagName("div")[0];
37560 var inner = el.getElementsByTagName("em")[0];
37561 return {"el": el, "close": close, "inner": inner};
37564 // not sure what this is..
37565 // if(!this.tabTpl){
37566 //this.tabTpl = new Roo.Template(
37567 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37568 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37570 // this.tabTpl = new Roo.Template(
37571 // '<a href="#">' +
37572 // '<span unselectable="on"' +
37573 // (this.disableTooltips ? '' : ' title="{text}"') +
37574 // ' >{text}</span></a>'
37580 var template = tpl || this.tabTpl || false;
37584 template = new Roo.Template(
37586 '<span unselectable="on"' +
37587 (this.disableTooltips ? '' : ' title="{text}"') +
37588 ' >{text}</span></a>'
37592 switch (typeof(template)) {
37596 template = new Roo.Template(template);
37602 var el = template.overwrite(td, {"text": text});
37604 var inner = el.getElementsByTagName("span")[0];
37606 return {"el": el, "inner": inner};
37614 * @class Roo.TabPanelItem
37615 * @extends Roo.util.Observable
37616 * Represents an individual item (tab plus body) in a TabPanel.
37617 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37618 * @param {String} id The id of this TabPanelItem
37619 * @param {String} text The text for the tab of this TabPanelItem
37620 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37622 Roo.bootstrap.panel.TabItem = function(config){
37624 * The {@link Roo.TabPanel} this TabPanelItem belongs to
37625 * @type Roo.TabPanel
37627 this.tabPanel = config.panel;
37629 * The id for this TabPanelItem
37632 this.id = config.id;
37634 this.disabled = false;
37636 this.text = config.text;
37638 this.loaded = false;
37639 this.closable = config.closable;
37642 * The body element for this TabPanelItem.
37643 * @type Roo.Element
37645 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37646 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37647 this.bodyEl.setStyle("display", "block");
37648 this.bodyEl.setStyle("zoom", "1");
37649 //this.hideAction();
37651 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37653 this.el = Roo.get(els.el);
37654 this.inner = Roo.get(els.inner, true);
37655 this.textEl = Roo.get(this.el.dom.firstChild, true);
37656 this.pnode = Roo.get(els.el.parentNode, true);
37657 this.el.on("mousedown", this.onTabMouseDown, this);
37658 this.el.on("click", this.onTabClick, this);
37660 if(config.closable){
37661 var c = Roo.get(els.close, true);
37662 c.dom.title = this.closeText;
37663 c.addClassOnOver("close-over");
37664 c.on("click", this.closeClick, this);
37670 * Fires when this tab becomes the active tab.
37671 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37672 * @param {Roo.TabPanelItem} this
37676 * @event beforeclose
37677 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37678 * @param {Roo.TabPanelItem} this
37679 * @param {Object} e Set cancel to true on this object to cancel the close.
37681 "beforeclose": true,
37684 * Fires when this tab is closed.
37685 * @param {Roo.TabPanelItem} this
37689 * @event deactivate
37690 * Fires when this tab is no longer the active tab.
37691 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37692 * @param {Roo.TabPanelItem} this
37694 "deactivate" : true
37696 this.hidden = false;
37698 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37701 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37703 purgeListeners : function(){
37704 Roo.util.Observable.prototype.purgeListeners.call(this);
37705 this.el.removeAllListeners();
37708 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37711 this.pnode.addClass("active");
37714 this.tabPanel.stripWrap.repaint();
37716 this.fireEvent("activate", this.tabPanel, this);
37720 * Returns true if this tab is the active tab.
37721 * @return {Boolean}
37723 isActive : function(){
37724 return this.tabPanel.getActiveTab() == this;
37728 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37731 this.pnode.removeClass("active");
37733 this.fireEvent("deactivate", this.tabPanel, this);
37736 hideAction : function(){
37737 this.bodyEl.hide();
37738 this.bodyEl.setStyle("position", "absolute");
37739 this.bodyEl.setLeft("-20000px");
37740 this.bodyEl.setTop("-20000px");
37743 showAction : function(){
37744 this.bodyEl.setStyle("position", "relative");
37745 this.bodyEl.setTop("");
37746 this.bodyEl.setLeft("");
37747 this.bodyEl.show();
37751 * Set the tooltip for the tab.
37752 * @param {String} tooltip The tab's tooltip
37754 setTooltip : function(text){
37755 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37756 this.textEl.dom.qtip = text;
37757 this.textEl.dom.removeAttribute('title');
37759 this.textEl.dom.title = text;
37763 onTabClick : function(e){
37764 e.preventDefault();
37765 this.tabPanel.activate(this.id);
37768 onTabMouseDown : function(e){
37769 e.preventDefault();
37770 this.tabPanel.activate(this.id);
37773 getWidth : function(){
37774 return this.inner.getWidth();
37777 setWidth : function(width){
37778 var iwidth = width - this.pnode.getPadding("lr");
37779 this.inner.setWidth(iwidth);
37780 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37781 this.pnode.setWidth(width);
37785 * Show or hide the tab
37786 * @param {Boolean} hidden True to hide or false to show.
37788 setHidden : function(hidden){
37789 this.hidden = hidden;
37790 this.pnode.setStyle("display", hidden ? "none" : "");
37794 * Returns true if this tab is "hidden"
37795 * @return {Boolean}
37797 isHidden : function(){
37798 return this.hidden;
37802 * Returns the text for this tab
37805 getText : function(){
37809 autoSize : function(){
37810 //this.el.beginMeasure();
37811 this.textEl.setWidth(1);
37813 * #2804 [new] Tabs in Roojs
37814 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37816 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37817 //this.el.endMeasure();
37821 * Sets the text for the tab (Note: this also sets the tooltip text)
37822 * @param {String} text The tab's text and tooltip
37824 setText : function(text){
37826 this.textEl.update(text);
37827 this.setTooltip(text);
37828 //if(!this.tabPanel.resizeTabs){
37829 // this.autoSize();
37833 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37835 activate : function(){
37836 this.tabPanel.activate(this.id);
37840 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37842 disable : function(){
37843 if(this.tabPanel.active != this){
37844 this.disabled = true;
37845 this.pnode.addClass("disabled");
37850 * Enables this TabPanelItem if it was previously disabled.
37852 enable : function(){
37853 this.disabled = false;
37854 this.pnode.removeClass("disabled");
37858 * Sets the content for this TabPanelItem.
37859 * @param {String} content The content
37860 * @param {Boolean} loadScripts true to look for and load scripts
37862 setContent : function(content, loadScripts){
37863 this.bodyEl.update(content, loadScripts);
37867 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37868 * @return {Roo.UpdateManager} The UpdateManager
37870 getUpdateManager : function(){
37871 return this.bodyEl.getUpdateManager();
37875 * Set a URL to be used to load the content for this TabPanelItem.
37876 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37877 * @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)
37878 * @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)
37879 * @return {Roo.UpdateManager} The UpdateManager
37881 setUrl : function(url, params, loadOnce){
37882 if(this.refreshDelegate){
37883 this.un('activate', this.refreshDelegate);
37885 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37886 this.on("activate", this.refreshDelegate);
37887 return this.bodyEl.getUpdateManager();
37891 _handleRefresh : function(url, params, loadOnce){
37892 if(!loadOnce || !this.loaded){
37893 var updater = this.bodyEl.getUpdateManager();
37894 updater.update(url, params, this._setLoaded.createDelegate(this));
37899 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
37900 * Will fail silently if the setUrl method has not been called.
37901 * This does not activate the panel, just updates its content.
37903 refresh : function(){
37904 if(this.refreshDelegate){
37905 this.loaded = false;
37906 this.refreshDelegate();
37911 _setLoaded : function(){
37912 this.loaded = true;
37916 closeClick : function(e){
37919 this.fireEvent("beforeclose", this, o);
37920 if(o.cancel !== true){
37921 this.tabPanel.removeTab(this.id);
37925 * The text displayed in the tooltip for the close icon.
37928 closeText : "Close this tab"
37931 * This script refer to:
37932 * Title: International Telephone Input
37933 * Author: Jack O'Connor
37934 * Code version: v12.1.12
37935 * Availability: https://github.com/jackocnr/intl-tel-input.git
37938 Roo.bootstrap.PhoneInput = function(config) {
37939 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37942 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37944 triggerList : true,
37946 listWidth: undefined,
37948 selectedClass: 'active',
37950 allCountries: false,
37952 dialCodeMapping: false,
37954 preferedCountries: false,
37956 defaultDialCode: '+852',
37958 getAutoCreate : function()
37960 var data = Roo.bootstrap.PhoneInputData;
37961 var align = this.labelAlign || this.parentLabelAlign();
37964 for (var i = 0; i < data.length; i++) {
37966 this.allCountries[i] = {
37970 priority: c[3] || 0,
37971 areaCodes: c[4] || null
37973 this.dialCodeMapping[c[2]] = {
37976 priority: c[3] || 0,
37977 areaCodes: c[4] || null
37990 cls : 'form-control tel-input',
37991 autocomplete: 'new-password'
37995 input.name = this.name;
37998 if (this.disabled) {
37999 input.disabled = true;
38002 var flag_container = {
38019 cls: this.hasFeedback ? 'has-feedback' : '',
38024 cls: 'dial-code-holder',
38031 cls: 'roo-select2-container input-group',
38038 if (this.fieldLabel.length) {
38041 tooltip: 'This field is required'
38047 cls: 'control-label',
38053 html: this.fieldLabel
38056 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
38062 if(this.indicatorpos == 'right') {
38063 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
38070 if(align == 'left') {
38078 if(this.labelWidth > 12){
38079 label.style = "width: " + this.labelWidth + 'px';
38081 if(this.labelWidth < 13 && this.labelmd == 0){
38082 this.labelmd = this.labelWidth;
38084 if(this.labellg > 0){
38085 label.cls += ' col-lg-' + this.labellg;
38086 input.cls += ' col-lg-' + (12 - this.labellg);
38088 if(this.labelmd > 0){
38089 label.cls += ' col-md-' + this.labelmd;
38090 container.cls += ' col-md-' + (12 - this.labelmd);
38092 if(this.labelsm > 0){
38093 label.cls += ' col-sm-' + this.labelsm;
38094 container.cls += ' col-sm-' + (12 - this.labelsm);
38096 if(this.labelxs > 0){
38097 label.cls += ' col-xs-' + this.labelxs;
38098 container.cls += ' col-xs-' + (12 - this.labelxs);
38108 var settings = this;
38110 ['xs','sm','md','lg'].map(function(size){
38111 if (settings[size]) {
38112 cfg.cls += ' col-' + size + '-' + settings[size];
38116 this.store = new Roo.data.Store({
38117 proxy : new Roo.data.MemoryProxy({}),
38118 reader : new Roo.data.JsonReader({
38129 'name' : 'dialCode',
38133 'name' : 'priority',
38137 'name' : 'areaCodes',
38144 this.store.proxy.data = {
38146 data: this.allCountries
38152 initEvents : function()
38155 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38157 this.indicator = this.indicatorEl();
38158 this.flag = this.flagEl();
38159 this.dialCodeHolder = this.dialCodeHolderEl();
38161 this.trigger = this.el.select('div.flag-box',true).first();
38162 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38167 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38168 _this.list.setWidth(lw);
38171 this.list.on('mouseover', this.onViewOver, this);
38172 this.list.on('mousemove', this.onViewMove, this);
38173 //this.list.on('scroll', this.onViewScroll, this);
38175 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
38177 this.view = new Roo.View(this.list, this.tpl, {
38178 singleSelect:true, store: this.store, selectedClass: this.selectedClass
38181 this.view.on('click', this.onViewClick, this);
38182 this.setValue(this.defaultDialCode);
38185 onTriggerClick : function(e)
38187 Roo.log('trigger click');
38188 if(this.disabled || !this.triggerList){
38192 if(this.isExpanded()){
38194 this.hasFocus = false;
38196 this.store.load({});
38197 this.hasFocus = true;
38202 isExpanded : function()
38204 return this.list.isVisible();
38207 collapse : function()
38209 if(!this.isExpanded()){
38213 Roo.get(document).un('mousedown', this.collapseIf, this);
38214 Roo.get(document).un('mousewheel', this.collapseIf, this);
38215 this.fireEvent('collapse', this);
38219 expand : function()
38223 if(this.isExpanded() || !this.hasFocus){
38227 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
38228 this.list.setWidth(lw);
38231 this.restrictHeight();
38233 Roo.get(document).on('mousedown', this.collapseIf, this);
38234 Roo.get(document).on('mousewheel', this.collapseIf, this);
38236 this.fireEvent('expand', this);
38239 restrictHeight : function()
38241 this.list.alignTo(this.inputEl(), this.listAlign);
38242 this.list.alignTo(this.inputEl(), this.listAlign);
38245 onViewOver : function(e, t)
38247 if(this.inKeyMode){
38250 var item = this.view.findItemFromChild(t);
38253 var index = this.view.indexOf(item);
38254 this.select(index, false);
38259 onViewClick : function(view, doFocus, el, e)
38261 var index = this.view.getSelectedIndexes()[0];
38263 var r = this.store.getAt(index);
38266 this.onSelect(r, index);
38268 if(doFocus !== false && !this.blockFocus){
38269 this.inputEl().focus();
38273 onViewMove : function(e, t)
38275 this.inKeyMode = false;
38278 select : function(index, scrollIntoView)
38280 this.selectedIndex = index;
38281 this.view.select(index);
38282 if(scrollIntoView !== false){
38283 var el = this.view.getNode(index);
38285 this.list.scrollChildIntoView(el, false);
38290 createList : function()
38292 this.list = Roo.get(document.body).createChild({
38294 cls: 'typeahead typeahead-long dropdown-menu tel-list',
38295 style: 'display:none'
38297 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
38300 collapseIf : function(e)
38302 var in_combo = e.within(this.el);
38303 var in_list = e.within(this.list);
38304 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
38306 if (in_combo || in_list || is_list) {
38312 onSelect : function(record, index)
38314 if(this.fireEvent('beforeselect', this, record, index) !== false){
38316 this.setFlagClass(record.data.iso2);
38317 this.setDialCode(record.data.dialCode);
38318 this.hasFocus = false;
38320 this.fireEvent('select', this, record, index);
38324 flagEl : function()
38326 var flag = this.el.select('div.flag',true).first();
38333 dialCodeHolderEl : function()
38335 var d = this.el.select('input.dial-code-holder',true).first();
38342 setDialCode : function(v)
38344 this.dialCodeHolder.dom.value = '+'+v;
38347 setFlagClass : function(n)
38349 this.flag.dom.className = 'flag '+n;
38352 getValue : function()
38354 var v = this.inputEl().getValue();
38355 if(this.dialCodeHolder) {
38356 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
38361 setValue : function(v)
38363 var d = this.getDialCode(v);
38366 if(!d || d.length == 0) {
38368 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
38373 this.setFlagClass(this.dialCodeMapping[d].iso2);
38374 this.setDialCode(d);
38375 this.inputEl().dom.value = v.replace('+'+d,'');
38378 getDialCode : function(v = '')
38380 if (v.length == 0) {
38381 return this.dialCodeHolder.dom.value;
38385 // only interested in international numbers (starting with a plus)
38386 if (v.charAt(0) != "+") {
38389 var numericChars = "";
38390 for (var i = 1; i < v.length; i++) {
38391 var c = v.charAt(i);
38394 if (this.dialCodeMapping[numericChars]) {
38395 dialCode = v.substr(1, i);
38397 if (numericChars.length == 4) {
38405 validate : function()