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
4573 * Create a new Navbar Button
4574 * @param {Object} config The config object
4576 Roo.bootstrap.NavSidebarItem = function(config){
4577 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4582 * The raw click event for the entire grid.
4583 * @param {Roo.EventObject} e
4588 * Fires when the active item active state changes
4589 * @param {Roo.bootstrap.NavSidebarItem} this
4590 * @param {boolean} state the new state
4598 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4600 badgeWeight : 'default',
4606 buttonWeight : 'default',
4610 getAutoCreate : function(){
4615 href : this.href || '#',
4621 if(this.buttonView){
4624 href : this.href || '#',
4625 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize,
4638 cfg.cls += ' active';
4641 if (this.disabled) {
4642 cfg.cls += ' disabled';
4645 cfg.cls += ' open x-open';
4648 if (this.glyphicon || this.icon) {
4649 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4650 a.cn.push({ tag : 'i', cls : c }) ;
4653 if(!this.buttonView){
4656 html : this.html || ''
4661 if (this.badge !== '') {
4662 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4670 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4671 a.cls += 'dropdown-toggle treeview' ;
4679 initEvents : function()
4681 if (typeof (this.menu) != 'undefined') {
4682 this.menu.parentType = this.xtype;
4683 this.menu.triggerEl = this.el;
4684 this.menu = this.addxtype(Roo.apply({}, this.menu));
4687 this.el.on('click', this.onClick, this);
4689 if(this.badge !== ''){
4691 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4696 onClick : function(e)
4703 if(this.preventDefault){
4707 this.fireEvent('click', this);
4710 disable : function()
4712 this.setDisabled(true);
4717 this.setDisabled(false);
4720 setDisabled : function(state)
4722 if(this.disabled == state){
4726 this.disabled = state;
4729 this.el.addClass('disabled');
4733 this.el.removeClass('disabled');
4738 setActive : function(state)
4740 if(this.active == state){
4744 this.active = state;
4747 this.el.addClass('active');
4751 this.el.removeClass('active');
4756 isActive: function ()
4761 setBadge : function(str)
4767 this.badgeEl.dom.innerHTML = str;
4784 * @class Roo.bootstrap.Row
4785 * @extends Roo.bootstrap.Component
4786 * Bootstrap Row class (contains columns...)
4790 * @param {Object} config The config object
4793 Roo.bootstrap.Row = function(config){
4794 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4799 getAutoCreate : function(){
4818 * @class Roo.bootstrap.Element
4819 * @extends Roo.bootstrap.Component
4820 * Bootstrap Element class
4821 * @cfg {String} html contents of the element
4822 * @cfg {String} tag tag of the element
4823 * @cfg {String} cls class of the element
4824 * @cfg {Boolean} preventDefault (true|false) default false
4825 * @cfg {Boolean} clickable (true|false) default false
4828 * Create a new Element
4829 * @param {Object} config The config object
4832 Roo.bootstrap.Element = function(config){
4833 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4839 * When a element is chick
4840 * @param {Roo.bootstrap.Element} this
4841 * @param {Roo.EventObject} e
4847 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4852 preventDefault: false,
4855 getAutoCreate : function(){
4866 initEvents: function()
4868 Roo.bootstrap.Element.superclass.initEvents.call(this);
4871 this.el.on('click', this.onClick, this);
4876 onClick : function(e)
4878 if(this.preventDefault){
4882 this.fireEvent('click', this, e);
4885 getValue : function()
4887 return this.el.dom.innerHTML;
4890 setValue : function(value)
4892 this.el.dom.innerHTML = value;
4907 * @class Roo.bootstrap.Pagination
4908 * @extends Roo.bootstrap.Component
4909 * Bootstrap Pagination class
4910 * @cfg {String} size xs | sm | md | lg
4911 * @cfg {Boolean} inverse false | true
4914 * Create a new Pagination
4915 * @param {Object} config The config object
4918 Roo.bootstrap.Pagination = function(config){
4919 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4928 getAutoCreate : function(){
4934 cfg.cls += ' inverse';
4940 cfg.cls += " " + this.cls;
4958 * @class Roo.bootstrap.PaginationItem
4959 * @extends Roo.bootstrap.Component
4960 * Bootstrap PaginationItem class
4961 * @cfg {String} html text
4962 * @cfg {String} href the link
4963 * @cfg {Boolean} preventDefault (true | false) default true
4964 * @cfg {Boolean} active (true | false) default false
4965 * @cfg {Boolean} disabled default false
4969 * Create a new PaginationItem
4970 * @param {Object} config The config object
4974 Roo.bootstrap.PaginationItem = function(config){
4975 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4980 * The raw click event for the entire grid.
4981 * @param {Roo.EventObject} e
4987 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4991 preventDefault: true,
4996 getAutoCreate : function(){
5002 href : this.href ? this.href : '#',
5003 html : this.html ? this.html : ''
5013 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5017 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5023 initEvents: function() {
5025 this.el.on('click', this.onClick, this);
5028 onClick : function(e)
5030 Roo.log('PaginationItem on click ');
5031 if(this.preventDefault){
5039 this.fireEvent('click', this, e);
5055 * @class Roo.bootstrap.Slider
5056 * @extends Roo.bootstrap.Component
5057 * Bootstrap Slider class
5060 * Create a new Slider
5061 * @param {Object} config The config object
5064 Roo.bootstrap.Slider = function(config){
5065 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5070 getAutoCreate : function(){
5074 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5078 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5090 * Ext JS Library 1.1.1
5091 * Copyright(c) 2006-2007, Ext JS, LLC.
5093 * Originally Released Under LGPL - original licence link has changed is not relivant.
5096 * <script type="text/javascript">
5101 * @class Roo.grid.ColumnModel
5102 * @extends Roo.util.Observable
5103 * This is the default implementation of a ColumnModel used by the Grid. It defines
5104 * the columns in the grid.
5107 var colModel = new Roo.grid.ColumnModel([
5108 {header: "Ticker", width: 60, sortable: true, locked: true},
5109 {header: "Company Name", width: 150, sortable: true},
5110 {header: "Market Cap.", width: 100, sortable: true},
5111 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5112 {header: "Employees", width: 100, sortable: true, resizable: false}
5117 * The config options listed for this class are options which may appear in each
5118 * individual column definition.
5119 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5121 * @param {Object} config An Array of column config objects. See this class's
5122 * config objects for details.
5124 Roo.grid.ColumnModel = function(config){
5126 * The config passed into the constructor
5128 this.config = config;
5131 // if no id, create one
5132 // if the column does not have a dataIndex mapping,
5133 // map it to the order it is in the config
5134 for(var i = 0, len = config.length; i < len; i++){
5136 if(typeof c.dataIndex == "undefined"){
5139 if(typeof c.renderer == "string"){
5140 c.renderer = Roo.util.Format[c.renderer];
5142 if(typeof c.id == "undefined"){
5145 if(c.editor && c.editor.xtype){
5146 c.editor = Roo.factory(c.editor, Roo.grid);
5148 if(c.editor && c.editor.isFormField){
5149 c.editor = new Roo.grid.GridEditor(c.editor);
5151 this.lookup[c.id] = c;
5155 * The width of columns which have no width specified (defaults to 100)
5158 this.defaultWidth = 100;
5161 * Default sortable of columns which have no sortable specified (defaults to false)
5164 this.defaultSortable = false;
5168 * @event widthchange
5169 * Fires when the width of a column changes.
5170 * @param {ColumnModel} this
5171 * @param {Number} columnIndex The column index
5172 * @param {Number} newWidth The new width
5174 "widthchange": true,
5176 * @event headerchange
5177 * Fires when the text of a header changes.
5178 * @param {ColumnModel} this
5179 * @param {Number} columnIndex The column index
5180 * @param {Number} newText The new header text
5182 "headerchange": true,
5184 * @event hiddenchange
5185 * Fires when a column is hidden or "unhidden".
5186 * @param {ColumnModel} this
5187 * @param {Number} columnIndex The column index
5188 * @param {Boolean} hidden true if hidden, false otherwise
5190 "hiddenchange": true,
5192 * @event columnmoved
5193 * Fires when a column is moved.
5194 * @param {ColumnModel} this
5195 * @param {Number} oldIndex
5196 * @param {Number} newIndex
5198 "columnmoved" : true,
5200 * @event columlockchange
5201 * Fires when a column's locked state is changed
5202 * @param {ColumnModel} this
5203 * @param {Number} colIndex
5204 * @param {Boolean} locked true if locked
5206 "columnlockchange" : true
5208 Roo.grid.ColumnModel.superclass.constructor.call(this);
5210 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5212 * @cfg {String} header The header text to display in the Grid view.
5215 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5216 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5217 * specified, the column's index is used as an index into the Record's data Array.
5220 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5221 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5225 * Defaults to the value of the {@link #defaultSortable} property.
5226 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5232 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5235 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5242 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5243 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5244 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5250 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5253 * @cfg {String} cursor (Optional)
5256 * @cfg {String} tooltip (Optional)
5259 * @cfg {Number} xs (Optional)
5262 * @cfg {Number} sm (Optional)
5265 * @cfg {Number} md (Optional)
5268 * @cfg {Number} lg (Optional)
5271 * Returns the id of the column at the specified index.
5272 * @param {Number} index The column index
5273 * @return {String} the id
5275 getColumnId : function(index){
5276 return this.config[index].id;
5280 * Returns the column for a specified id.
5281 * @param {String} id The column id
5282 * @return {Object} the column
5284 getColumnById : function(id){
5285 return this.lookup[id];
5290 * Returns the column for a specified dataIndex.
5291 * @param {String} dataIndex The column dataIndex
5292 * @return {Object|Boolean} the column or false if not found
5294 getColumnByDataIndex: function(dataIndex){
5295 var index = this.findColumnIndex(dataIndex);
5296 return index > -1 ? this.config[index] : false;
5300 * Returns the index for a specified column id.
5301 * @param {String} id The column id
5302 * @return {Number} the index, or -1 if not found
5304 getIndexById : function(id){
5305 for(var i = 0, len = this.config.length; i < len; i++){
5306 if(this.config[i].id == id){
5314 * Returns the index for a specified column dataIndex.
5315 * @param {String} dataIndex The column dataIndex
5316 * @return {Number} the index, or -1 if not found
5319 findColumnIndex : function(dataIndex){
5320 for(var i = 0, len = this.config.length; i < len; i++){
5321 if(this.config[i].dataIndex == dataIndex){
5329 moveColumn : function(oldIndex, newIndex){
5330 var c = this.config[oldIndex];
5331 this.config.splice(oldIndex, 1);
5332 this.config.splice(newIndex, 0, c);
5333 this.dataMap = null;
5334 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337 isLocked : function(colIndex){
5338 return this.config[colIndex].locked === true;
5341 setLocked : function(colIndex, value, suppressEvent){
5342 if(this.isLocked(colIndex) == value){
5345 this.config[colIndex].locked = value;
5347 this.fireEvent("columnlockchange", this, colIndex, value);
5351 getTotalLockedWidth : function(){
5353 for(var i = 0; i < this.config.length; i++){
5354 if(this.isLocked(i) && !this.isHidden(i)){
5355 this.totalWidth += this.getColumnWidth(i);
5361 getLockedCount : function(){
5362 for(var i = 0, len = this.config.length; i < len; i++){
5363 if(!this.isLocked(i)){
5368 return this.config.length;
5372 * Returns the number of columns.
5375 getColumnCount : function(visibleOnly){
5376 if(visibleOnly === true){
5378 for(var i = 0, len = this.config.length; i < len; i++){
5379 if(!this.isHidden(i)){
5385 return this.config.length;
5389 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5390 * @param {Function} fn
5391 * @param {Object} scope (optional)
5392 * @return {Array} result
5394 getColumnsBy : function(fn, scope){
5396 for(var i = 0, len = this.config.length; i < len; i++){
5397 var c = this.config[i];
5398 if(fn.call(scope||this, c, i) === true){
5406 * Returns true if the specified column is sortable.
5407 * @param {Number} col The column index
5410 isSortable : function(col){
5411 if(typeof this.config[col].sortable == "undefined"){
5412 return this.defaultSortable;
5414 return this.config[col].sortable;
5418 * Returns the rendering (formatting) function defined for the column.
5419 * @param {Number} col The column index.
5420 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5422 getRenderer : function(col){
5423 if(!this.config[col].renderer){
5424 return Roo.grid.ColumnModel.defaultRenderer;
5426 return this.config[col].renderer;
5430 * Sets the rendering (formatting) function for a column.
5431 * @param {Number} col The column index
5432 * @param {Function} fn The function to use to process the cell's raw data
5433 * to return HTML markup for the grid view. The render function is called with
5434 * the following parameters:<ul>
5435 * <li>Data value.</li>
5436 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5437 * <li>css A CSS style string to apply to the table cell.</li>
5438 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5439 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5440 * <li>Row index</li>
5441 * <li>Column index</li>
5442 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5444 setRenderer : function(col, fn){
5445 this.config[col].renderer = fn;
5449 * Returns the width for the specified column.
5450 * @param {Number} col The column index
5453 getColumnWidth : function(col){
5454 return this.config[col].width * 1 || this.defaultWidth;
5458 * Sets the width for a column.
5459 * @param {Number} col The column index
5460 * @param {Number} width The new width
5462 setColumnWidth : function(col, width, suppressEvent){
5463 this.config[col].width = width;
5464 this.totalWidth = null;
5466 this.fireEvent("widthchange", this, col, width);
5471 * Returns the total width of all columns.
5472 * @param {Boolean} includeHidden True to include hidden column widths
5475 getTotalWidth : function(includeHidden){
5476 if(!this.totalWidth){
5477 this.totalWidth = 0;
5478 for(var i = 0, len = this.config.length; i < len; i++){
5479 if(includeHidden || !this.isHidden(i)){
5480 this.totalWidth += this.getColumnWidth(i);
5484 return this.totalWidth;
5488 * Returns the header for the specified column.
5489 * @param {Number} col The column index
5492 getColumnHeader : function(col){
5493 return this.config[col].header;
5497 * Sets the header for a column.
5498 * @param {Number} col The column index
5499 * @param {String} header The new header
5501 setColumnHeader : function(col, header){
5502 this.config[col].header = header;
5503 this.fireEvent("headerchange", this, col, header);
5507 * Returns the tooltip for the specified column.
5508 * @param {Number} col The column index
5511 getColumnTooltip : function(col){
5512 return this.config[col].tooltip;
5515 * Sets the tooltip for a column.
5516 * @param {Number} col The column index
5517 * @param {String} tooltip The new tooltip
5519 setColumnTooltip : function(col, tooltip){
5520 this.config[col].tooltip = tooltip;
5524 * Returns the dataIndex for the specified column.
5525 * @param {Number} col The column index
5528 getDataIndex : function(col){
5529 return this.config[col].dataIndex;
5533 * Sets the dataIndex for a column.
5534 * @param {Number} col The column index
5535 * @param {Number} dataIndex The new dataIndex
5537 setDataIndex : function(col, dataIndex){
5538 this.config[col].dataIndex = dataIndex;
5544 * Returns true if the cell is editable.
5545 * @param {Number} colIndex The column index
5546 * @param {Number} rowIndex The row index - this is nto actually used..?
5549 isCellEditable : function(colIndex, rowIndex){
5550 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5554 * Returns the editor defined for the cell/column.
5555 * return false or null to disable editing.
5556 * @param {Number} colIndex The column index
5557 * @param {Number} rowIndex The row index
5560 getCellEditor : function(colIndex, rowIndex){
5561 return this.config[colIndex].editor;
5565 * Sets if a column is editable.
5566 * @param {Number} col The column index
5567 * @param {Boolean} editable True if the column is editable
5569 setEditable : function(col, editable){
5570 this.config[col].editable = editable;
5575 * Returns true if the column is hidden.
5576 * @param {Number} colIndex The column index
5579 isHidden : function(colIndex){
5580 return this.config[colIndex].hidden;
5585 * Returns true if the column width cannot be changed
5587 isFixed : function(colIndex){
5588 return this.config[colIndex].fixed;
5592 * Returns true if the column can be resized
5595 isResizable : function(colIndex){
5596 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599 * Sets if a column is hidden.
5600 * @param {Number} colIndex The column index
5601 * @param {Boolean} hidden True if the column is hidden
5603 setHidden : function(colIndex, hidden){
5604 this.config[colIndex].hidden = hidden;
5605 this.totalWidth = null;
5606 this.fireEvent("hiddenchange", this, colIndex, hidden);
5610 * Sets the editor for a column.
5611 * @param {Number} col The column index
5612 * @param {Object} editor The editor object
5614 setEditor : function(col, editor){
5615 this.config[col].editor = editor;
5619 Roo.grid.ColumnModel.defaultRenderer = function(value)
5621 if(typeof value == "object") {
5624 if(typeof value == "string" && value.length < 1){
5628 return String.format("{0}", value);
5631 // Alias for backwards compatibility
5632 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 * Ext JS Library 1.1.1
5636 * Copyright(c) 2006-2007, Ext JS, LLC.
5638 * Originally Released Under LGPL - original licence link has changed is not relivant.
5641 * <script type="text/javascript">
5645 * @class Roo.LoadMask
5646 * A simple utility class for generically masking elements while loading data. If the element being masked has
5647 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5648 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5649 * element's UpdateManager load indicator and will be destroyed after the initial load.
5651 * Create a new LoadMask
5652 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5653 * @param {Object} config The config object
5655 Roo.LoadMask = function(el, config){
5656 this.el = Roo.get(el);
5657 Roo.apply(this, config);
5659 this.store.on('beforeload', this.onBeforeLoad, this);
5660 this.store.on('load', this.onLoad, this);
5661 this.store.on('loadexception', this.onLoadException, this);
5662 this.removeMask = false;
5664 var um = this.el.getUpdateManager();
5665 um.showLoadIndicator = false; // disable the default indicator
5666 um.on('beforeupdate', this.onBeforeLoad, this);
5667 um.on('update', this.onLoad, this);
5668 um.on('failure', this.onLoad, this);
5669 this.removeMask = true;
5673 Roo.LoadMask.prototype = {
5675 * @cfg {Boolean} removeMask
5676 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5677 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5681 * The text to display in a centered loading message box (defaults to 'Loading...')
5685 * @cfg {String} msgCls
5686 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5688 msgCls : 'x-mask-loading',
5691 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5697 * Disables the mask to prevent it from being displayed
5699 disable : function(){
5700 this.disabled = true;
5704 * Enables the mask so that it can be displayed
5706 enable : function(){
5707 this.disabled = false;
5710 onLoadException : function()
5714 if (typeof(arguments[3]) != 'undefined') {
5715 Roo.MessageBox.alert("Error loading",arguments[3]);
5719 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5720 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5727 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5732 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5736 onBeforeLoad : function(){
5738 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5743 destroy : function(){
5745 this.store.un('beforeload', this.onBeforeLoad, this);
5746 this.store.un('load', this.onLoad, this);
5747 this.store.un('loadexception', this.onLoadException, this);
5749 var um = this.el.getUpdateManager();
5750 um.un('beforeupdate', this.onBeforeLoad, this);
5751 um.un('update', this.onLoad, this);
5752 um.un('failure', this.onLoad, this);
5763 * @class Roo.bootstrap.Table
5764 * @extends Roo.bootstrap.Component
5765 * Bootstrap Table class
5766 * @cfg {String} cls table class
5767 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5768 * @cfg {String} bgcolor Specifies the background color for a table
5769 * @cfg {Number} border Specifies whether the table cells should have borders or not
5770 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5771 * @cfg {Number} cellspacing Specifies the space between cells
5772 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5773 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5774 * @cfg {String} sortable Specifies that the table should be sortable
5775 * @cfg {String} summary Specifies a summary of the content of a table
5776 * @cfg {Number} width Specifies the width of a table
5777 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5779 * @cfg {boolean} striped Should the rows be alternative striped
5780 * @cfg {boolean} bordered Add borders to the table
5781 * @cfg {boolean} hover Add hover highlighting
5782 * @cfg {boolean} condensed Format condensed
5783 * @cfg {boolean} responsive Format condensed
5784 * @cfg {Boolean} loadMask (true|false) default false
5785 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5786 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5787 * @cfg {Boolean} rowSelection (true|false) default false
5788 * @cfg {Boolean} cellSelection (true|false) default false
5789 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5790 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5791 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5795 * Create a new Table
5796 * @param {Object} config The config object
5799 Roo.bootstrap.Table = function(config){
5800 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5805 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5806 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5807 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5808 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5810 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5812 this.sm.grid = this;
5813 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5814 this.sm = this.selModel;
5815 this.sm.xmodule = this.xmodule || false;
5818 if (this.cm && typeof(this.cm.config) == 'undefined') {
5819 this.colModel = new Roo.grid.ColumnModel(this.cm);
5820 this.cm = this.colModel;
5821 this.cm.xmodule = this.xmodule || false;
5824 this.store= Roo.factory(this.store, Roo.data);
5825 this.ds = this.store;
5826 this.ds.xmodule = this.xmodule || false;
5829 if (this.footer && this.store) {
5830 this.footer.dataSource = this.ds;
5831 this.footer = Roo.factory(this.footer);
5838 * Fires when a cell is clicked
5839 * @param {Roo.bootstrap.Table} this
5840 * @param {Roo.Element} el
5841 * @param {Number} rowIndex
5842 * @param {Number} columnIndex
5843 * @param {Roo.EventObject} e
5847 * @event celldblclick
5848 * Fires when a cell is double clicked
5849 * @param {Roo.bootstrap.Table} this
5850 * @param {Roo.Element} el
5851 * @param {Number} rowIndex
5852 * @param {Number} columnIndex
5853 * @param {Roo.EventObject} e
5855 "celldblclick" : true,
5858 * Fires when a row is clicked
5859 * @param {Roo.bootstrap.Table} this
5860 * @param {Roo.Element} el
5861 * @param {Number} rowIndex
5862 * @param {Roo.EventObject} e
5866 * @event rowdblclick
5867 * Fires when a row is double clicked
5868 * @param {Roo.bootstrap.Table} this
5869 * @param {Roo.Element} el
5870 * @param {Number} rowIndex
5871 * @param {Roo.EventObject} e
5873 "rowdblclick" : true,
5876 * Fires when a mouseover occur
5877 * @param {Roo.bootstrap.Table} this
5878 * @param {Roo.Element} el
5879 * @param {Number} rowIndex
5880 * @param {Number} columnIndex
5881 * @param {Roo.EventObject} e
5886 * Fires when a mouseout occur
5887 * @param {Roo.bootstrap.Table} this
5888 * @param {Roo.Element} el
5889 * @param {Number} rowIndex
5890 * @param {Number} columnIndex
5891 * @param {Roo.EventObject} e
5896 * Fires when a row is rendered, so you can change add a style to it.
5897 * @param {Roo.bootstrap.Table} this
5898 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5902 * @event rowsrendered
5903 * Fires when all the rows have been rendered
5904 * @param {Roo.bootstrap.Table} this
5906 'rowsrendered' : true,
5908 * @event contextmenu
5909 * The raw contextmenu event for the entire grid.
5910 * @param {Roo.EventObject} e
5912 "contextmenu" : true,
5914 * @event rowcontextmenu
5915 * Fires when a row is right clicked
5916 * @param {Roo.bootstrap.Table} this
5917 * @param {Number} rowIndex
5918 * @param {Roo.EventObject} e
5920 "rowcontextmenu" : true,
5922 * @event cellcontextmenu
5923 * Fires when a cell is right clicked
5924 * @param {Roo.bootstrap.Table} this
5925 * @param {Number} rowIndex
5926 * @param {Number} cellIndex
5927 * @param {Roo.EventObject} e
5929 "cellcontextmenu" : true,
5931 * @event headercontextmenu
5932 * Fires when a header is right clicked
5933 * @param {Roo.bootstrap.Table} this
5934 * @param {Number} columnIndex
5935 * @param {Roo.EventObject} e
5937 "headercontextmenu" : true
5941 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5967 rowSelection : false,
5968 cellSelection : false,
5971 // Roo.Element - the tbody
5973 // Roo.Element - thead element
5976 container: false, // used by gridpanel...
5980 getAutoCreate : function()
5982 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5989 if (this.scrollBody) {
5990 cfg.cls += ' table-body-fixed';
5993 cfg.cls += ' table-striped';
5997 cfg.cls += ' table-hover';
5999 if (this.bordered) {
6000 cfg.cls += ' table-bordered';
6002 if (this.condensed) {
6003 cfg.cls += ' table-condensed';
6005 if (this.responsive) {
6006 cfg.cls += ' table-responsive';
6010 cfg.cls+= ' ' +this.cls;
6013 // this lot should be simplifed...
6016 cfg.align=this.align;
6019 cfg.bgcolor=this.bgcolor;
6022 cfg.border=this.border;
6024 if (this.cellpadding) {
6025 cfg.cellpadding=this.cellpadding;
6027 if (this.cellspacing) {
6028 cfg.cellspacing=this.cellspacing;
6031 cfg.frame=this.frame;
6034 cfg.rules=this.rules;
6036 if (this.sortable) {
6037 cfg.sortable=this.sortable;
6040 cfg.summary=this.summary;
6043 cfg.width=this.width;
6046 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049 if(this.store || this.cm){
6050 if(this.headerShow){
6051 cfg.cn.push(this.renderHeader());
6054 cfg.cn.push(this.renderBody());
6056 if(this.footerShow){
6057 cfg.cn.push(this.renderFooter());
6059 // where does this come from?
6060 //cfg.cls+= ' TableGrid';
6063 return { cn : [ cfg ] };
6066 initEvents : function()
6068 if(!this.store || !this.cm){
6071 if (this.selModel) {
6072 this.selModel.initEvents();
6076 //Roo.log('initEvents with ds!!!!');
6078 this.mainBody = this.el.select('tbody', true).first();
6079 this.mainHead = this.el.select('thead', true).first();
6086 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6087 e.on('click', _this.sort, _this);
6090 this.mainBody.on("click", this.onClick, this);
6091 this.mainBody.on("dblclick", this.onDblClick, this);
6093 // why is this done????? = it breaks dialogs??
6094 //this.parent().el.setStyle('position', 'relative');
6098 this.footer.parentId = this.id;
6099 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102 this.el.select('tfoot tr td').first().addClass('hide');
6106 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6108 this.store.on('load', this.onLoad, this);
6109 this.store.on('beforeload', this.onBeforeLoad, this);
6110 this.store.on('update', this.onUpdate, this);
6111 this.store.on('add', this.onAdd, this);
6112 this.store.on("clear", this.clear, this);
6114 this.el.on("contextmenu", this.onContextMenu, this);
6116 this.mainBody.on('scroll', this.onBodyScroll, this);
6121 onContextMenu : function(e, t)
6123 this.processEvent("contextmenu", e);
6126 processEvent : function(name, e)
6128 if (name != 'touchstart' ) {
6129 this.fireEvent(name, e);
6132 var t = e.getTarget();
6134 var cell = Roo.get(t);
6140 if(cell.findParent('tfoot', false, true)){
6144 if(cell.findParent('thead', false, true)){
6146 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6147 cell = Roo.get(t).findParent('th', false, true);
6149 Roo.log("failed to find th in thead?");
6150 Roo.log(e.getTarget());
6155 var cellIndex = cell.dom.cellIndex;
6157 var ename = name == 'touchstart' ? 'click' : name;
6158 this.fireEvent("header" + ename, this, cellIndex, e);
6163 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6164 cell = Roo.get(t).findParent('td', false, true);
6166 Roo.log("failed to find th in tbody?");
6167 Roo.log(e.getTarget());
6172 var row = cell.findParent('tr', false, true);
6173 var cellIndex = cell.dom.cellIndex;
6174 var rowIndex = row.dom.rowIndex - 1;
6178 this.fireEvent("row" + name, this, rowIndex, e);
6182 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6188 onMouseover : function(e, el)
6190 var cell = Roo.get(el);
6196 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6197 cell = cell.findParent('td', false, true);
6200 var row = cell.findParent('tr', false, true);
6201 var cellIndex = cell.dom.cellIndex;
6202 var rowIndex = row.dom.rowIndex - 1; // start from 0
6204 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6208 onMouseout : function(e, el)
6210 var cell = Roo.get(el);
6216 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6217 cell = cell.findParent('td', false, true);
6220 var row = cell.findParent('tr', false, true);
6221 var cellIndex = cell.dom.cellIndex;
6222 var rowIndex = row.dom.rowIndex - 1; // start from 0
6224 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6228 onClick : function(e, el)
6230 var cell = Roo.get(el);
6232 if(!cell || (!this.cellSelection && !this.rowSelection)){
6236 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6237 cell = cell.findParent('td', false, true);
6240 if(!cell || typeof(cell) == 'undefined'){
6244 var row = cell.findParent('tr', false, true);
6246 if(!row || typeof(row) == 'undefined'){
6250 var cellIndex = cell.dom.cellIndex;
6251 var rowIndex = this.getRowIndex(row);
6253 // why??? - should these not be based on SelectionModel?
6254 if(this.cellSelection){
6255 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258 if(this.rowSelection){
6259 this.fireEvent('rowclick', this, row, rowIndex, e);
6265 onDblClick : function(e,el)
6267 var cell = Roo.get(el);
6269 if(!cell || (!this.cellSelection && !this.rowSelection)){
6273 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6274 cell = cell.findParent('td', false, true);
6277 if(!cell || typeof(cell) == 'undefined'){
6281 var row = cell.findParent('tr', false, true);
6283 if(!row || typeof(row) == 'undefined'){
6287 var cellIndex = cell.dom.cellIndex;
6288 var rowIndex = this.getRowIndex(row);
6290 if(this.cellSelection){
6291 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294 if(this.rowSelection){
6295 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6299 sort : function(e,el)
6301 var col = Roo.get(el);
6303 if(!col.hasClass('sortable')){
6307 var sort = col.attr('sort');
6310 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6314 this.store.sortInfo = {field : sort, direction : dir};
6317 Roo.log("calling footer first");
6318 this.footer.onClick('first');
6321 this.store.load({ params : { start : 0 } });
6325 renderHeader : function()
6333 this.totalWidth = 0;
6335 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6337 var config = cm.config[i];
6342 html: cm.getColumnHeader(i)
6347 if(typeof(config.sortable) != 'undefined' && config.sortable){
6349 c.html = '<i class="glyphicon"></i>' + c.html;
6352 if(typeof(config.lgHeader) != 'undefined'){
6353 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356 if(typeof(config.mdHeader) != 'undefined'){
6357 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360 if(typeof(config.smHeader) != 'undefined'){
6361 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364 if(typeof(config.xsHeader) != 'undefined'){
6365 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6372 if(typeof(config.tooltip) != 'undefined'){
6373 c.tooltip = config.tooltip;
6376 if(typeof(config.colspan) != 'undefined'){
6377 c.colspan = config.colspan;
6380 if(typeof(config.hidden) != 'undefined' && config.hidden){
6381 c.style += ' display:none;';
6384 if(typeof(config.dataIndex) != 'undefined'){
6385 c.sort = config.dataIndex;
6390 if(typeof(config.align) != 'undefined' && config.align.length){
6391 c.style += ' text-align:' + config.align + ';';
6394 if(typeof(config.width) != 'undefined'){
6395 c.style += ' width:' + config.width + 'px;';
6396 this.totalWidth += config.width;
6398 this.totalWidth += 100; // assume minimum of 100 per column?
6401 if(typeof(config.cls) != 'undefined'){
6402 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405 ['xs','sm','md','lg'].map(function(size){
6407 if(typeof(config[size]) == 'undefined'){
6411 if (!config[size]) { // 0 = hidden
6412 c.cls += ' hidden-' + size;
6416 c.cls += ' col-' + size + '-' + config[size];
6426 renderBody : function()
6436 colspan : this.cm.getColumnCount()
6446 renderFooter : function()
6456 colspan : this.cm.getColumnCount()
6470 // Roo.log('ds onload');
6475 var ds = this.store;
6477 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6478 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6479 if (_this.store.sortInfo) {
6481 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6482 e.select('i', true).addClass(['glyphicon-arrow-up']);
6485 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6486 e.select('i', true).addClass(['glyphicon-arrow-down']);
6491 var tbody = this.mainBody;
6493 if(ds.getCount() > 0){
6494 ds.data.each(function(d,rowIndex){
6495 var row = this.renderRow(cm, ds, rowIndex);
6497 tbody.createChild(row);
6501 if(row.cellObjects.length){
6502 Roo.each(row.cellObjects, function(r){
6503 _this.renderCellObject(r);
6510 Roo.each(this.el.select('tbody td', true).elements, function(e){
6511 e.on('mouseover', _this.onMouseover, _this);
6514 Roo.each(this.el.select('tbody td', true).elements, function(e){
6515 e.on('mouseout', _this.onMouseout, _this);
6517 this.fireEvent('rowsrendered', this);
6518 //if(this.loadMask){
6519 // this.maskEl.hide();
6526 onUpdate : function(ds,record)
6528 this.refreshRow(record);
6532 onRemove : function(ds, record, index, isUpdate){
6533 if(isUpdate !== true){
6534 this.fireEvent("beforerowremoved", this, index, record);
6536 var bt = this.mainBody.dom;
6538 var rows = this.el.select('tbody > tr', true).elements;
6540 if(typeof(rows[index]) != 'undefined'){
6541 bt.removeChild(rows[index].dom);
6544 // if(bt.rows[index]){
6545 // bt.removeChild(bt.rows[index]);
6548 if(isUpdate !== true){
6549 //this.stripeRows(index);
6550 //this.syncRowHeights(index, index);
6552 this.fireEvent("rowremoved", this, index, record);
6556 onAdd : function(ds, records, rowIndex)
6558 //Roo.log('on Add called');
6559 // - note this does not handle multiple adding very well..
6560 var bt = this.mainBody.dom;
6561 for (var i =0 ; i < records.length;i++) {
6562 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6563 //Roo.log(records[i]);
6564 //Roo.log(this.store.getAt(rowIndex+i));
6565 this.insertRow(this.store, rowIndex + i, false);
6572 refreshRow : function(record){
6573 var ds = this.store, index;
6574 if(typeof record == 'number'){
6576 record = ds.getAt(index);
6578 index = ds.indexOf(record);
6580 this.insertRow(ds, index, true);
6582 this.onRemove(ds, record, index+1, true);
6584 //this.syncRowHeights(index, index);
6586 this.fireEvent("rowupdated", this, index, record);
6589 insertRow : function(dm, rowIndex, isUpdate){
6592 this.fireEvent("beforerowsinserted", this, rowIndex);
6594 //var s = this.getScrollState();
6595 var row = this.renderRow(this.cm, this.store, rowIndex);
6596 // insert before rowIndex..
6597 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6601 if(row.cellObjects.length){
6602 Roo.each(row.cellObjects, function(r){
6603 _this.renderCellObject(r);
6608 this.fireEvent("rowsinserted", this, rowIndex);
6609 //this.syncRowHeights(firstRow, lastRow);
6610 //this.stripeRows(firstRow);
6617 getRowDom : function(rowIndex)
6619 var rows = this.el.select('tbody > tr', true).elements;
6621 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624 // returns the object tree for a tr..
6627 renderRow : function(cm, ds, rowIndex)
6630 var d = ds.getAt(rowIndex);
6637 var cellObjects = [];
6639 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6640 var config = cm.config[i];
6642 var renderer = cm.getRenderer(i);
6646 if(typeof(renderer) !== 'undefined'){
6647 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6649 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6650 // and are rendered into the cells after the row is rendered - using the id for the element.
6652 if(typeof(value) === 'object'){
6662 rowIndex : rowIndex,
6667 this.fireEvent('rowclass', this, rowcfg);
6671 cls : rowcfg.rowClass,
6673 html: (typeof(value) === 'object') ? '' : value
6680 if(typeof(config.colspan) != 'undefined'){
6681 td.colspan = config.colspan;
6684 if(typeof(config.hidden) != 'undefined' && config.hidden){
6685 td.style += ' display:none;';
6688 if(typeof(config.align) != 'undefined' && config.align.length){
6689 td.style += ' text-align:' + config.align + ';';
6692 if(typeof(config.width) != 'undefined'){
6693 td.style += ' width:' + config.width + 'px;';
6696 if(typeof(config.cursor) != 'undefined'){
6697 td.style += ' cursor:' + config.cursor + ';';
6700 if(typeof(config.cls) != 'undefined'){
6701 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704 ['xs','sm','md','lg'].map(function(size){
6706 if(typeof(config[size]) == 'undefined'){
6710 if (!config[size]) { // 0 = hidden
6711 td.cls += ' hidden-' + size;
6715 td.cls += ' col-' + size + '-' + config[size];
6723 row.cellObjects = cellObjects;
6731 onBeforeLoad : function()
6733 //Roo.log('ds onBeforeLoad');
6737 //if(this.loadMask){
6738 // this.maskEl.show();
6746 this.el.select('tbody', true).first().dom.innerHTML = '';
6749 * Show or hide a row.
6750 * @param {Number} rowIndex to show or hide
6751 * @param {Boolean} state hide
6753 setRowVisibility : function(rowIndex, state)
6755 var bt = this.mainBody.dom;
6757 var rows = this.el.select('tbody > tr', true).elements;
6759 if(typeof(rows[rowIndex]) == 'undefined'){
6762 rows[rowIndex].dom.style.display = state ? '' : 'none';
6766 getSelectionModel : function(){
6768 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6770 return this.selModel;
6773 * Render the Roo.bootstrap object from renderder
6775 renderCellObject : function(r)
6779 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6781 var t = r.cfg.render(r.container);
6784 Roo.each(r.cfg.cn, function(c){
6786 container: t.getChildContainer(),
6789 _this.renderCellObject(child);
6794 getRowIndex : function(row)
6798 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6809 * Returns the grid's underlying element = used by panel.Grid
6810 * @return {Element} The element
6812 getGridEl : function(){
6816 * Forces a resize - used by panel.Grid
6817 * @return {Element} The element
6819 autoSize : function()
6821 //var ctr = Roo.get(this.container.dom.parentElement);
6822 var ctr = Roo.get(this.el.dom);
6824 var thd = this.getGridEl().select('thead',true).first();
6825 var tbd = this.getGridEl().select('tbody', true).first();
6826 var tfd = this.getGridEl().select('tfoot', true).first();
6828 var cw = ctr.getWidth();
6832 tbd.setSize(ctr.getWidth(),
6833 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6835 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838 cw = Math.max(cw, this.totalWidth);
6839 this.getGridEl().select('tr',true).setWidth(cw);
6840 // resize 'expandable coloumn?
6842 return; // we doe not have a view in this design..
6845 onBodyScroll: function()
6847 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6848 this.mainHead.setStyle({
6849 'position' : 'relative',
6850 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6855 var scrollHeight = this.mainBody.dom.scrollHeight;
6857 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6859 var height = this.mainBody.getHeight();
6861 if(scrollHeight - height == scrollTop) {
6863 var total = this.ds.getTotalCount();
6865 if(this.footer.cursor + this.footer.pageSize < total){
6867 this.footer.ds.load({
6869 start : this.footer.cursor + this.footer.pageSize,
6870 limit : this.footer.pageSize
6891 * @class Roo.bootstrap.TableCell
6892 * @extends Roo.bootstrap.Component
6893 * Bootstrap TableCell class
6894 * @cfg {String} html cell contain text
6895 * @cfg {String} cls cell class
6896 * @cfg {String} tag cell tag (td|th) default td
6897 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6898 * @cfg {String} align Aligns the content in a cell
6899 * @cfg {String} axis Categorizes cells
6900 * @cfg {String} bgcolor Specifies the background color of a cell
6901 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6902 * @cfg {Number} colspan Specifies the number of columns a cell should span
6903 * @cfg {String} headers Specifies one or more header cells a cell is related to
6904 * @cfg {Number} height Sets the height of a cell
6905 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6906 * @cfg {Number} rowspan Sets the number of rows a cell should span
6907 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6908 * @cfg {String} valign Vertical aligns the content in a cell
6909 * @cfg {Number} width Specifies the width of a cell
6912 * Create a new TableCell
6913 * @param {Object} config The config object
6916 Roo.bootstrap.TableCell = function(config){
6917 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6940 getAutoCreate : function(){
6941 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6961 cfg.align=this.align
6967 cfg.bgcolor=this.bgcolor
6970 cfg.charoff=this.charoff
6973 cfg.colspan=this.colspan
6976 cfg.headers=this.headers
6979 cfg.height=this.height
6982 cfg.nowrap=this.nowrap
6985 cfg.rowspan=this.rowspan
6988 cfg.scope=this.scope
6991 cfg.valign=this.valign
6994 cfg.width=this.width
7013 * @class Roo.bootstrap.TableRow
7014 * @extends Roo.bootstrap.Component
7015 * Bootstrap TableRow class
7016 * @cfg {String} cls row class
7017 * @cfg {String} align Aligns the content in a table row
7018 * @cfg {String} bgcolor Specifies a background color for a table row
7019 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7020 * @cfg {String} valign Vertical aligns the content in a table row
7023 * Create a new TableRow
7024 * @param {Object} config The config object
7027 Roo.bootstrap.TableRow = function(config){
7028 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7039 getAutoCreate : function(){
7040 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7050 cfg.align = this.align;
7053 cfg.bgcolor = this.bgcolor;
7056 cfg.charoff = this.charoff;
7059 cfg.valign = this.valign;
7077 * @class Roo.bootstrap.TableBody
7078 * @extends Roo.bootstrap.Component
7079 * Bootstrap TableBody class
7080 * @cfg {String} cls element class
7081 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7082 * @cfg {String} align Aligns the content inside the element
7083 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7084 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087 * Create a new TableBody
7088 * @param {Object} config The config object
7091 Roo.bootstrap.TableBody = function(config){
7092 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7103 getAutoCreate : function(){
7104 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7118 cfg.align = this.align;
7121 cfg.charoff = this.charoff;
7124 cfg.valign = this.valign;
7131 // initEvents : function()
7138 // this.store = Roo.factory(this.store, Roo.data);
7139 // this.store.on('load', this.onLoad, this);
7141 // this.store.load();
7145 // onLoad: function ()
7147 // this.fireEvent('load', this);
7157 * Ext JS Library 1.1.1
7158 * Copyright(c) 2006-2007, Ext JS, LLC.
7160 * Originally Released Under LGPL - original licence link has changed is not relivant.
7163 * <script type="text/javascript">
7166 // as we use this in bootstrap.
7167 Roo.namespace('Roo.form');
7169 * @class Roo.form.Action
7170 * Internal Class used to handle form actions
7172 * @param {Roo.form.BasicForm} el The form element or its id
7173 * @param {Object} config Configuration options
7178 // define the action interface
7179 Roo.form.Action = function(form, options){
7181 this.options = options || {};
7184 * Client Validation Failed
7187 Roo.form.Action.CLIENT_INVALID = 'client';
7189 * Server Validation Failed
7192 Roo.form.Action.SERVER_INVALID = 'server';
7194 * Connect to Server Failed
7197 Roo.form.Action.CONNECT_FAILURE = 'connect';
7199 * Reading Data from Server Failed
7202 Roo.form.Action.LOAD_FAILURE = 'load';
7204 Roo.form.Action.prototype = {
7206 failureType : undefined,
7207 response : undefined,
7211 run : function(options){
7216 success : function(response){
7221 handleResponse : function(response){
7225 // default connection failure
7226 failure : function(response){
7228 this.response = response;
7229 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7230 this.form.afterAction(this, false);
7233 processResponse : function(response){
7234 this.response = response;
7235 if(!response.responseText){
7238 this.result = this.handleResponse(response);
7242 // utility functions used internally
7243 getUrl : function(appendParams){
7244 var url = this.options.url || this.form.url || this.form.el.dom.action;
7246 var p = this.getParams();
7248 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7254 getMethod : function(){
7255 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258 getParams : function(){
7259 var bp = this.form.baseParams;
7260 var p = this.options.params;
7262 if(typeof p == "object"){
7263 p = Roo.urlEncode(Roo.applyIf(p, bp));
7264 }else if(typeof p == 'string' && bp){
7265 p += '&' + Roo.urlEncode(bp);
7268 p = Roo.urlEncode(bp);
7273 createCallback : function(){
7275 success: this.success,
7276 failure: this.failure,
7278 timeout: (this.form.timeout*1000),
7279 upload: this.form.fileUpload ? this.success : undefined
7284 Roo.form.Action.Submit = function(form, options){
7285 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291 haveProgress : false,
7292 uploadComplete : false,
7294 // uploadProgress indicator.
7295 uploadProgress : function()
7297 if (!this.form.progressUrl) {
7301 if (!this.haveProgress) {
7302 Roo.MessageBox.progress("Uploading", "Uploading");
7304 if (this.uploadComplete) {
7305 Roo.MessageBox.hide();
7309 this.haveProgress = true;
7311 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7313 var c = new Roo.data.Connection();
7315 url : this.form.progressUrl,
7320 success : function(req){
7321 //console.log(data);
7325 rdata = Roo.decode(req.responseText)
7327 Roo.log("Invalid data from server..");
7331 if (!rdata || !rdata.success) {
7333 Roo.MessageBox.alert(Roo.encode(rdata));
7336 var data = rdata.data;
7338 if (this.uploadComplete) {
7339 Roo.MessageBox.hide();
7344 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7345 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348 this.uploadProgress.defer(2000,this);
7351 failure: function(data) {
7352 Roo.log('progress url failed ');
7363 // run get Values on the form, so it syncs any secondary forms.
7364 this.form.getValues();
7366 var o = this.options;
7367 var method = this.getMethod();
7368 var isPost = method == 'POST';
7369 if(o.clientValidation === false || this.form.isValid()){
7371 if (this.form.progressUrl) {
7372 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7373 (new Date() * 1) + '' + Math.random());
7378 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7379 form:this.form.el.dom,
7380 url:this.getUrl(!isPost),
7382 params:isPost ? this.getParams() : null,
7383 isUpload: this.form.fileUpload
7386 this.uploadProgress();
7388 }else if (o.clientValidation !== false){ // client validation failed
7389 this.failureType = Roo.form.Action.CLIENT_INVALID;
7390 this.form.afterAction(this, false);
7394 success : function(response)
7396 this.uploadComplete= true;
7397 if (this.haveProgress) {
7398 Roo.MessageBox.hide();
7402 var result = this.processResponse(response);
7403 if(result === true || result.success){
7404 this.form.afterAction(this, true);
7408 this.form.markInvalid(result.errors);
7409 this.failureType = Roo.form.Action.SERVER_INVALID;
7411 this.form.afterAction(this, false);
7413 failure : function(response)
7415 this.uploadComplete= true;
7416 if (this.haveProgress) {
7417 Roo.MessageBox.hide();
7420 this.response = response;
7421 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7422 this.form.afterAction(this, false);
7425 handleResponse : function(response){
7426 if(this.form.errorReader){
7427 var rs = this.form.errorReader.read(response);
7430 for(var i = 0, len = rs.records.length; i < len; i++) {
7431 var r = rs.records[i];
7435 if(errors.length < 1){
7439 success : rs.success,
7445 ret = Roo.decode(response.responseText);
7449 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7459 Roo.form.Action.Load = function(form, options){
7460 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7461 this.reader = this.form.reader;
7464 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7469 Roo.Ajax.request(Roo.apply(
7470 this.createCallback(), {
7471 method:this.getMethod(),
7472 url:this.getUrl(false),
7473 params:this.getParams()
7477 success : function(response){
7479 var result = this.processResponse(response);
7480 if(result === true || !result.success || !result.data){
7481 this.failureType = Roo.form.Action.LOAD_FAILURE;
7482 this.form.afterAction(this, false);
7485 this.form.clearInvalid();
7486 this.form.setValues(result.data);
7487 this.form.afterAction(this, true);
7490 handleResponse : function(response){
7491 if(this.form.reader){
7492 var rs = this.form.reader.read(response);
7493 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7495 success : rs.success,
7499 return Roo.decode(response.responseText);
7503 Roo.form.Action.ACTION_TYPES = {
7504 'load' : Roo.form.Action.Load,
7505 'submit' : Roo.form.Action.Submit
7514 * @class Roo.bootstrap.Form
7515 * @extends Roo.bootstrap.Component
7516 * Bootstrap Form class
7517 * @cfg {String} method GET | POST (default POST)
7518 * @cfg {String} labelAlign top | left (default top)
7519 * @cfg {String} align left | right - for navbars
7520 * @cfg {Boolean} loadMask load mask when submit (default true)
7525 * @param {Object} config The config object
7529 Roo.bootstrap.Form = function(config){
7530 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7532 Roo.bootstrap.Form.popover.apply();
7536 * @event clientvalidation
7537 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7538 * @param {Form} this
7539 * @param {Boolean} valid true if the form has passed client-side validation
7541 clientvalidation: true,
7543 * @event beforeaction
7544 * Fires before any action is performed. Return false to cancel the action.
7545 * @param {Form} this
7546 * @param {Action} action The action to be performed
7550 * @event actionfailed
7551 * Fires when an action fails.
7552 * @param {Form} this
7553 * @param {Action} action The action that failed
7555 actionfailed : true,
7557 * @event actioncomplete
7558 * Fires when an action is completed.
7559 * @param {Form} this
7560 * @param {Action} action The action that completed
7562 actioncomplete : true
7567 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7570 * @cfg {String} method
7571 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7576 * The URL to use for form actions if one isn't supplied in the action options.
7579 * @cfg {Boolean} fileUpload
7580 * Set to true if this form is a file upload.
7584 * @cfg {Object} baseParams
7585 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7589 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7593 * @cfg {Sting} align (left|right) for navbar forms
7598 activeAction : null,
7601 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7602 * element by passing it or its id or mask the form itself by passing in true.
7605 waitMsgTarget : false,
7610 * @cfg {Boolean} errorMask (true|false) default false
7615 * @cfg {Number} maskOffset Default 100
7619 getAutoCreate : function(){
7623 method : this.method || 'POST',
7624 id : this.id || Roo.id(),
7627 if (this.parent().xtype.match(/^Nav/)) {
7628 cfg.cls = 'navbar-form navbar-' + this.align;
7632 if (this.labelAlign == 'left' ) {
7633 cfg.cls += ' form-horizontal';
7639 initEvents : function()
7641 this.el.on('submit', this.onSubmit, this);
7642 // this was added as random key presses on the form where triggering form submit.
7643 this.el.on('keypress', function(e) {
7644 if (e.getCharCode() != 13) {
7647 // we might need to allow it for textareas.. and some other items.
7648 // check e.getTarget().
7650 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7654 Roo.log("keypress blocked");
7662 onSubmit : function(e){
7667 * Returns true if client-side validation on the form is successful.
7670 isValid : function(){
7671 var items = this.getItems();
7675 items.each(function(f){
7682 if(!target && f.el.isVisible(true)){
7688 if(this.errorMask && !valid){
7689 Roo.bootstrap.Form.popover.mask(this, target);
7696 * Returns true if any fields in this form have changed since their original load.
7699 isDirty : function(){
7701 var items = this.getItems();
7702 items.each(function(f){
7712 * Performs a predefined action (submit or load) or custom actions you define on this form.
7713 * @param {String} actionName The name of the action type
7714 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7715 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7716 * accept other config options):
7718 Property Type Description
7719 ---------------- --------------- ----------------------------------------------------------------------------------
7720 url String The url for the action (defaults to the form's url)
7721 method String The form method to use (defaults to the form's method, or POST if not defined)
7722 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7723 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7724 validate the form on the client (defaults to false)
7726 * @return {BasicForm} this
7728 doAction : function(action, options){
7729 if(typeof action == 'string'){
7730 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7732 if(this.fireEvent('beforeaction', this, action) !== false){
7733 this.beforeAction(action);
7734 action.run.defer(100, action);
7740 beforeAction : function(action){
7741 var o = action.options;
7744 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7746 // not really supported yet.. ??
7748 //if(this.waitMsgTarget === true){
7749 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7750 //}else if(this.waitMsgTarget){
7751 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7752 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7754 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7760 afterAction : function(action, success){
7761 this.activeAction = null;
7762 var o = action.options;
7764 //if(this.waitMsgTarget === true){
7766 //}else if(this.waitMsgTarget){
7767 // this.waitMsgTarget.unmask();
7769 // Roo.MessageBox.updateProgress(1);
7770 // Roo.MessageBox.hide();
7777 Roo.callback(o.success, o.scope, [this, action]);
7778 this.fireEvent('actioncomplete', this, action);
7782 // failure condition..
7783 // we have a scenario where updates need confirming.
7784 // eg. if a locking scenario exists..
7785 // we look for { errors : { needs_confirm : true }} in the response.
7787 (typeof(action.result) != 'undefined') &&
7788 (typeof(action.result.errors) != 'undefined') &&
7789 (typeof(action.result.errors.needs_confirm) != 'undefined')
7792 Roo.log("not supported yet");
7795 Roo.MessageBox.confirm(
7796 "Change requires confirmation",
7797 action.result.errorMsg,
7802 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7812 Roo.callback(o.failure, o.scope, [this, action]);
7813 // show an error message if no failed handler is set..
7814 if (!this.hasListener('actionfailed')) {
7815 Roo.log("need to add dialog support");
7817 Roo.MessageBox.alert("Error",
7818 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7819 action.result.errorMsg :
7820 "Saving Failed, please check your entries or try again"
7825 this.fireEvent('actionfailed', this, action);
7830 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7831 * @param {String} id The value to search for
7834 findField : function(id){
7835 var items = this.getItems();
7836 var field = items.get(id);
7838 items.each(function(f){
7839 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7846 return field || null;
7849 * Mark fields in this form invalid in bulk.
7850 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7851 * @return {BasicForm} this
7853 markInvalid : function(errors){
7854 if(errors instanceof Array){
7855 for(var i = 0, len = errors.length; i < len; i++){
7856 var fieldError = errors[i];
7857 var f = this.findField(fieldError.id);
7859 f.markInvalid(fieldError.msg);
7865 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7866 field.markInvalid(errors[id]);
7870 //Roo.each(this.childForms || [], function (f) {
7871 // f.markInvalid(errors);
7878 * Set values for fields in this form in bulk.
7879 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7880 * @return {BasicForm} this
7882 setValues : function(values){
7883 if(values instanceof Array){ // array of objects
7884 for(var i = 0, len = values.length; i < len; i++){
7886 var f = this.findField(v.id);
7888 f.setValue(v.value);
7889 if(this.trackResetOnLoad){
7890 f.originalValue = f.getValue();
7894 }else{ // object hash
7897 if(typeof values[id] != 'function' && (field = this.findField(id))){
7899 if (field.setFromData &&
7901 field.displayField &&
7902 // combos' with local stores can
7903 // be queried via setValue()
7904 // to set their value..
7905 (field.store && !field.store.isLocal)
7909 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7910 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7911 field.setFromData(sd);
7914 field.setValue(values[id]);
7918 if(this.trackResetOnLoad){
7919 field.originalValue = field.getValue();
7925 //Roo.each(this.childForms || [], function (f) {
7926 // f.setValues(values);
7933 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7934 * they are returned as an array.
7935 * @param {Boolean} asString
7938 getValues : function(asString){
7939 //if (this.childForms) {
7940 // copy values from the child forms
7941 // Roo.each(this.childForms, function (f) {
7942 // this.setValues(f.getValues());
7948 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7949 if(asString === true){
7952 return Roo.urlDecode(fs);
7956 * Returns the fields in this form as an object with key/value pairs.
7957 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7960 getFieldValues : function(with_hidden)
7962 var items = this.getItems();
7964 items.each(function(f){
7968 var v = f.getValue();
7969 if (f.inputType =='radio') {
7970 if (typeof(ret[f.getName()]) == 'undefined') {
7971 ret[f.getName()] = ''; // empty..
7974 if (!f.el.dom.checked) {
7982 // not sure if this supported any more..
7983 if ((typeof(v) == 'object') && f.getRawValue) {
7984 v = f.getRawValue() ; // dates..
7986 // combo boxes where name != hiddenName...
7987 if (f.name !== false && f.name != '' && f.name != f.getName()) {
7988 ret[f.name] = f.getRawValue();
7990 ret[f.getName()] = v;
7997 * Clears all invalid messages in this form.
7998 * @return {BasicForm} this
8000 clearInvalid : function(){
8001 var items = this.getItems();
8003 items.each(function(f){
8014 * @return {BasicForm} this
8017 var items = this.getItems();
8018 items.each(function(f){
8022 Roo.each(this.childForms || [], function (f) {
8029 getItems : function()
8031 var r=new Roo.util.MixedCollection(false, function(o){
8032 return o.id || (o.id = Roo.id());
8034 var iter = function(el) {
8041 Roo.each(el.items,function(e) {
8058 Roo.apply(Roo.bootstrap.Form, {
8085 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8086 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8087 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8088 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8091 this.maskEl.top.enableDisplayMode("block");
8092 this.maskEl.left.enableDisplayMode("block");
8093 this.maskEl.bottom.enableDisplayMode("block");
8094 this.maskEl.right.enableDisplayMode("block");
8096 this.toolTip = new Roo.bootstrap.Tooltip({
8097 cls : 'roo-form-error-popover',
8099 'left' : ['r-l', [-2,0], 'right'],
8100 'right' : ['l-r', [2,0], 'left'],
8101 'bottom' : ['tl-bl', [0,2], 'top'],
8102 'top' : [ 'bl-tl', [0,-2], 'bottom']
8106 this.toolTip.render(Roo.get(document.body));
8108 this.toolTip.el.enableDisplayMode("block");
8110 Roo.get(document.body).on('click', function(){
8114 Roo.get(document.body).on('touchstart', function(){
8118 this.isApplied = true
8121 mask : function(form, target)
8125 this.target = target;
8127 if(!this.form.errorMask || !target.el){
8131 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8133 var ot = this.target.el.calcOffsetsTo(scrollable);
8135 var scrollTo = ot[1] - this.form.maskOffset;
8137 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8139 scrollable.scrollTo('top', scrollTo);
8141 var box = this.target.el.getBox();
8143 var zIndex = Roo.bootstrap.Modal.zIndex++;
8146 this.maskEl.top.setStyle('position', 'absolute');
8147 this.maskEl.top.setStyle('z-index', zIndex);
8148 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8149 this.maskEl.top.setLeft(0);
8150 this.maskEl.top.setTop(0);
8151 this.maskEl.top.show();
8153 this.maskEl.left.setStyle('position', 'absolute');
8154 this.maskEl.left.setStyle('z-index', zIndex);
8155 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8156 this.maskEl.left.setLeft(0);
8157 this.maskEl.left.setTop(box.y - this.padding);
8158 this.maskEl.left.show();
8160 this.maskEl.bottom.setStyle('position', 'absolute');
8161 this.maskEl.bottom.setStyle('z-index', zIndex);
8162 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8163 this.maskEl.bottom.setLeft(0);
8164 this.maskEl.bottom.setTop(box.bottom + this.padding);
8165 this.maskEl.bottom.show();
8167 this.maskEl.right.setStyle('position', 'absolute');
8168 this.maskEl.right.setStyle('z-index', zIndex);
8169 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8170 this.maskEl.right.setLeft(box.right + this.padding);
8171 this.maskEl.right.setTop(box.y - this.padding);
8172 this.maskEl.right.show();
8174 this.toolTip.bindEl = this.target.el;
8176 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8178 var tip = this.target.blankText;
8180 if(this.target.getValue() !== '' ) {
8182 if (this.target.invalidText.length) {
8183 tip = this.target.invalidText;
8184 } else if (this.target.regexText.length){
8185 tip = this.target.regexText;
8189 this.toolTip.show(tip);
8191 this.intervalID = window.setInterval(function() {
8192 Roo.bootstrap.Form.popover.unmask();
8195 window.onwheel = function(){ return false;};
8197 (function(){ this.isMasked = true; }).defer(500, this);
8203 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8207 this.maskEl.top.setStyle('position', 'absolute');
8208 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8209 this.maskEl.top.hide();
8211 this.maskEl.left.setStyle('position', 'absolute');
8212 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8213 this.maskEl.left.hide();
8215 this.maskEl.bottom.setStyle('position', 'absolute');
8216 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8217 this.maskEl.bottom.hide();
8219 this.maskEl.right.setStyle('position', 'absolute');
8220 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8221 this.maskEl.right.hide();
8223 this.toolTip.hide();
8225 this.toolTip.el.hide();
8227 window.onwheel = function(){ return true;};
8229 if(this.intervalID){
8230 window.clearInterval(this.intervalID);
8231 this.intervalID = false;
8234 this.isMasked = false;
8244 * Ext JS Library 1.1.1
8245 * Copyright(c) 2006-2007, Ext JS, LLC.
8247 * Originally Released Under LGPL - original licence link has changed is not relivant.
8250 * <script type="text/javascript">
8253 * @class Roo.form.VTypes
8254 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8257 Roo.form.VTypes = function(){
8258 // closure these in so they are only created once.
8259 var alpha = /^[a-zA-Z_]+$/;
8260 var alphanum = /^[a-zA-Z0-9_]+$/;
8261 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8262 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8264 // All these messages and functions are configurable
8267 * The function used to validate email addresses
8268 * @param {String} value The email address
8270 'email' : function(v){
8271 return email.test(v);
8274 * The error text to display when the email validation function returns false
8277 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8279 * The keystroke filter mask to be applied on email input
8282 'emailMask' : /[a-z0-9_\.\-@]/i,
8285 * The function used to validate URLs
8286 * @param {String} value The URL
8288 'url' : function(v){
8292 * The error text to display when the url validation function returns false
8295 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8298 * The function used to validate alpha values
8299 * @param {String} value The value
8301 'alpha' : function(v){
8302 return alpha.test(v);
8305 * The error text to display when the alpha validation function returns false
8308 'alphaText' : 'This field should only contain letters and _',
8310 * The keystroke filter mask to be applied on alpha input
8313 'alphaMask' : /[a-z_]/i,
8316 * The function used to validate alphanumeric values
8317 * @param {String} value The value
8319 'alphanum' : function(v){
8320 return alphanum.test(v);
8323 * The error text to display when the alphanumeric validation function returns false
8326 'alphanumText' : 'This field should only contain letters, numbers and _',
8328 * The keystroke filter mask to be applied on alphanumeric input
8331 'alphanumMask' : /[a-z0-9_]/i
8341 * @class Roo.bootstrap.Input
8342 * @extends Roo.bootstrap.Component
8343 * Bootstrap Input class
8344 * @cfg {Boolean} disabled is it disabled
8345 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8346 * @cfg {String} name name of the input
8347 * @cfg {string} fieldLabel - the label associated
8348 * @cfg {string} placeholder - placeholder to put in text.
8349 * @cfg {string} before - input group add on before
8350 * @cfg {string} after - input group add on after
8351 * @cfg {string} size - (lg|sm) or leave empty..
8352 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8353 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8354 * @cfg {Number} md colspan out of 12 for computer-sized screens
8355 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8356 * @cfg {string} value default value of the input
8357 * @cfg {Number} labelWidth set the width of label
8358 * @cfg {Number} labellg set the width of label (1-12)
8359 * @cfg {Number} labelmd set the width of label (1-12)
8360 * @cfg {Number} labelsm set the width of label (1-12)
8361 * @cfg {Number} labelxs set the width of label (1-12)
8362 * @cfg {String} labelAlign (top|left)
8363 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8364 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8365 * @cfg {String} indicatorpos (left|right) default left
8367 * @cfg {String} align (left|center|right) Default left
8368 * @cfg {Boolean} forceFeedback (true|false) Default false
8374 * Create a new Input
8375 * @param {Object} config The config object
8378 Roo.bootstrap.Input = function(config){
8380 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8385 * Fires when this field receives input focus.
8386 * @param {Roo.form.Field} this
8391 * Fires when this field loses input focus.
8392 * @param {Roo.form.Field} this
8397 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8398 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8399 * @param {Roo.form.Field} this
8400 * @param {Roo.EventObject} e The event object
8405 * Fires just before the field blurs if the field value has changed.
8406 * @param {Roo.form.Field} this
8407 * @param {Mixed} newValue The new value
8408 * @param {Mixed} oldValue The original value
8413 * Fires after the field has been marked as invalid.
8414 * @param {Roo.form.Field} this
8415 * @param {String} msg The validation message
8420 * Fires after the field has been validated with no errors.
8421 * @param {Roo.form.Field} this
8426 * Fires after the key up
8427 * @param {Roo.form.Field} this
8428 * @param {Roo.EventObject} e The event Object
8434 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8436 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8437 automatic validation (defaults to "keyup").
8439 validationEvent : "keyup",
8441 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8443 validateOnBlur : true,
8445 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8447 validationDelay : 250,
8449 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8451 focusClass : "x-form-focus", // not needed???
8455 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8457 invalidClass : "has-warning",
8460 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8462 validClass : "has-success",
8465 * @cfg {Boolean} hasFeedback (true|false) default true
8470 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8472 invalidFeedbackClass : "glyphicon-warning-sign",
8475 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8477 validFeedbackClass : "glyphicon-ok",
8480 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8482 selectOnFocus : false,
8485 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8489 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8494 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8496 disableKeyFilter : false,
8499 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8503 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8507 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8509 blankText : "Please complete this mandatory field",
8512 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8516 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8518 maxLength : Number.MAX_VALUE,
8520 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8522 minLengthText : "The minimum length for this field is {0}",
8524 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8526 maxLengthText : "The maximum length for this field is {0}",
8530 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8531 * If available, this function will be called only after the basic validators all return true, and will be passed the
8532 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8536 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8537 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8538 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8542 * @cfg {String} regexText -- Depricated - use Invalid Text
8547 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8553 autocomplete: false,
8572 formatedValue : false,
8573 forceFeedback : false,
8575 indicatorpos : 'left',
8582 parentLabelAlign : function()
8585 while (parent.parent()) {
8586 parent = parent.parent();
8587 if (typeof(parent.labelAlign) !='undefined') {
8588 return parent.labelAlign;
8595 getAutoCreate : function()
8597 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8603 if(this.inputType != 'hidden'){
8604 cfg.cls = 'form-group' //input-group
8610 type : this.inputType,
8612 cls : 'form-control',
8613 placeholder : this.placeholder || '',
8614 autocomplete : this.autocomplete || 'new-password'
8618 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8621 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8622 input.maxLength = this.maxLength;
8625 if (this.disabled) {
8626 input.disabled=true;
8629 if (this.readOnly) {
8630 input.readonly=true;
8634 input.name = this.name;
8638 input.cls += ' input-' + this.size;
8642 ['xs','sm','md','lg'].map(function(size){
8643 if (settings[size]) {
8644 cfg.cls += ' col-' + size + '-' + settings[size];
8648 var inputblock = input;
8652 cls: 'glyphicon form-control-feedback'
8655 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8658 cls : 'has-feedback',
8666 if (this.before || this.after) {
8669 cls : 'input-group',
8673 if (this.before && typeof(this.before) == 'string') {
8675 inputblock.cn.push({
8677 cls : 'roo-input-before input-group-addon',
8681 if (this.before && typeof(this.before) == 'object') {
8682 this.before = Roo.factory(this.before);
8684 inputblock.cn.push({
8686 cls : 'roo-input-before input-group-' +
8687 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8691 inputblock.cn.push(input);
8693 if (this.after && typeof(this.after) == 'string') {
8694 inputblock.cn.push({
8696 cls : 'roo-input-after input-group-addon',
8700 if (this.after && typeof(this.after) == 'object') {
8701 this.after = Roo.factory(this.after);
8703 inputblock.cn.push({
8705 cls : 'roo-input-after input-group-' +
8706 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8710 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8711 inputblock.cls += ' has-feedback';
8712 inputblock.cn.push(feedback);
8716 if (align ==='left' && this.fieldLabel.length) {
8718 cfg.cls += ' roo-form-group-label-left';
8723 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8724 tooltip : 'This field is required'
8729 cls : 'control-label',
8730 html : this.fieldLabel
8741 var labelCfg = cfg.cn[1];
8742 var contentCfg = cfg.cn[2];
8744 if(this.indicatorpos == 'right'){
8749 cls : 'control-label',
8750 html : this.fieldLabel
8755 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8756 tooltip : 'This field is required'
8767 labelCfg = cfg.cn[0];
8768 contentCfg = cfg.cn[2];
8772 if(this.labelWidth > 12){
8773 labelCfg.style = "width: " + this.labelWidth + 'px';
8776 if(this.labelWidth < 13 && this.labelmd == 0){
8777 this.labelmd = this.labelWidth;
8780 if(this.labellg > 0){
8781 labelCfg.cls += ' col-lg-' + this.labellg;
8782 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8785 if(this.labelmd > 0){
8786 labelCfg.cls += ' col-md-' + this.labelmd;
8787 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8790 if(this.labelsm > 0){
8791 labelCfg.cls += ' col-sm-' + this.labelsm;
8792 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8795 if(this.labelxs > 0){
8796 labelCfg.cls += ' col-xs-' + this.labelxs;
8797 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8801 } else if ( this.fieldLabel.length) {
8806 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8807 tooltip : 'This field is required'
8811 //cls : 'input-group-addon',
8812 html : this.fieldLabel
8820 if(this.indicatorpos == 'right'){
8825 //cls : 'input-group-addon',
8826 html : this.fieldLabel
8831 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8832 tooltip : 'This field is required'
8852 if (this.parentType === 'Navbar' && this.parent().bar) {
8853 cfg.cls += ' navbar-form';
8856 if (this.parentType === 'NavGroup') {
8857 cfg.cls += ' navbar-form';
8865 * return the real input element.
8867 inputEl: function ()
8869 return this.el.select('input.form-control',true).first();
8872 tooltipEl : function()
8874 return this.inputEl();
8877 indicatorEl : function()
8879 var indicator = this.el.select('i.roo-required-indicator',true).first();
8889 setDisabled : function(v)
8891 var i = this.inputEl().dom;
8893 i.removeAttribute('disabled');
8897 i.setAttribute('disabled','true');
8899 initEvents : function()
8902 this.inputEl().on("keydown" , this.fireKey, this);
8903 this.inputEl().on("focus", this.onFocus, this);
8904 this.inputEl().on("blur", this.onBlur, this);
8906 this.inputEl().relayEvent('keyup', this);
8908 this.indicator = this.indicatorEl();
8911 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8912 this.indicator.hide();
8915 // reference to original value for reset
8916 this.originalValue = this.getValue();
8917 //Roo.form.TextField.superclass.initEvents.call(this);
8918 if(this.validationEvent == 'keyup'){
8919 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8920 this.inputEl().on('keyup', this.filterValidation, this);
8922 else if(this.validationEvent !== false){
8923 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8926 if(this.selectOnFocus){
8927 this.on("focus", this.preFocus, this);
8930 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8931 this.inputEl().on("keypress", this.filterKeys, this);
8933 this.inputEl().relayEvent('keypress', this);
8936 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8937 this.el.on("click", this.autoSize, this);
8940 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8941 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8944 if (typeof(this.before) == 'object') {
8945 this.before.render(this.el.select('.roo-input-before',true).first());
8947 if (typeof(this.after) == 'object') {
8948 this.after.render(this.el.select('.roo-input-after',true).first());
8953 filterValidation : function(e){
8954 if(!e.isNavKeyPress()){
8955 this.validationTask.delay(this.validationDelay);
8959 * Validates the field value
8960 * @return {Boolean} True if the value is valid, else false
8962 validate : function(){
8963 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8964 if(this.disabled || this.validateValue(this.getRawValue())){
8975 * Validates a value according to the field's validation rules and marks the field as invalid
8976 * if the validation fails
8977 * @param {Mixed} value The value to validate
8978 * @return {Boolean} True if the value is valid, else false
8980 validateValue : function(value){
8981 if(value.length < 1) { // if it's blank
8982 if(this.allowBlank){
8985 return this.inputEl().hasClass('hide') ? true : false;
8988 if(value.length < this.minLength){
8991 if(value.length > this.maxLength){
8995 var vt = Roo.form.VTypes;
8996 if(!vt[this.vtype](value, this)){
9000 if(typeof this.validator == "function"){
9001 var msg = this.validator(value);
9005 if (typeof(msg) == 'string') {
9006 this.invalidText = msg;
9010 if(this.regex && !this.regex.test(value)){
9020 fireKey : function(e){
9021 //Roo.log('field ' + e.getKey());
9022 if(e.isNavKeyPress()){
9023 this.fireEvent("specialkey", this, e);
9026 focus : function (selectText){
9028 this.inputEl().focus();
9029 if(selectText === true){
9030 this.inputEl().dom.select();
9036 onFocus : function(){
9037 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9038 // this.el.addClass(this.focusClass);
9041 this.hasFocus = true;
9042 this.startValue = this.getValue();
9043 this.fireEvent("focus", this);
9047 beforeBlur : Roo.emptyFn,
9051 onBlur : function(){
9053 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9054 //this.el.removeClass(this.focusClass);
9056 this.hasFocus = false;
9057 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9060 var v = this.getValue();
9061 if(String(v) !== String(this.startValue)){
9062 this.fireEvent('change', this, v, this.startValue);
9064 this.fireEvent("blur", this);
9068 * Resets the current field value to the originally loaded value and clears any validation messages
9071 this.setValue(this.originalValue);
9075 * Returns the name of the field
9076 * @return {Mixed} name The name field
9078 getName: function(){
9082 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9083 * @return {Mixed} value The field value
9085 getValue : function(){
9087 var v = this.inputEl().getValue();
9092 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9093 * @return {Mixed} value The field value
9095 getRawValue : function(){
9096 var v = this.inputEl().getValue();
9102 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9103 * @param {Mixed} value The value to set
9105 setRawValue : function(v){
9106 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9109 selectText : function(start, end){
9110 var v = this.getRawValue();
9112 start = start === undefined ? 0 : start;
9113 end = end === undefined ? v.length : end;
9114 var d = this.inputEl().dom;
9115 if(d.setSelectionRange){
9116 d.setSelectionRange(start, end);
9117 }else if(d.createTextRange){
9118 var range = d.createTextRange();
9119 range.moveStart("character", start);
9120 range.moveEnd("character", v.length-end);
9127 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9128 * @param {Mixed} value The value to set
9130 setValue : function(v){
9133 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9139 processValue : function(value){
9140 if(this.stripCharsRe){
9141 var newValue = value.replace(this.stripCharsRe, '');
9142 if(newValue !== value){
9143 this.setRawValue(newValue);
9150 preFocus : function(){
9152 if(this.selectOnFocus){
9153 this.inputEl().dom.select();
9156 filterKeys : function(e){
9158 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9161 var c = e.getCharCode(), cc = String.fromCharCode(c);
9162 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9165 if(!this.maskRe.test(cc)){
9170 * Clear any invalid styles/messages for this field
9172 clearInvalid : function(){
9174 if(!this.el || this.preventMark){ // not rendered
9179 this.el.removeClass(this.invalidClass);
9181 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9183 var feedback = this.el.select('.form-control-feedback', true).first();
9186 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9191 this.fireEvent('valid', this);
9195 * Mark this field as valid
9197 markValid : function()
9199 if(!this.el || this.preventMark){ // not rendered...
9203 this.el.removeClass([this.invalidClass, this.validClass]);
9205 var feedback = this.el.select('.form-control-feedback', true).first();
9208 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9215 if(this.allowBlank && !this.getRawValue().length){
9220 this.indicator.hide();
9223 this.el.addClass(this.validClass);
9225 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9227 var feedback = this.el.select('.form-control-feedback', true).first();
9230 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9231 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9236 this.fireEvent('valid', this);
9240 * Mark this field as invalid
9241 * @param {String} msg The validation message
9243 markInvalid : function(msg)
9245 if(!this.el || this.preventMark){ // not rendered
9249 this.el.removeClass([this.invalidClass, this.validClass]);
9251 var feedback = this.el.select('.form-control-feedback', true).first();
9254 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9261 if(this.allowBlank && !this.getRawValue().length){
9266 this.indicator.show();
9269 this.el.addClass(this.invalidClass);
9271 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9273 var feedback = this.el.select('.form-control-feedback', true).first();
9276 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9278 if(this.getValue().length || this.forceFeedback){
9279 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9286 this.fireEvent('invalid', this, msg);
9289 SafariOnKeyDown : function(event)
9291 // this is a workaround for a password hang bug on chrome/ webkit.
9292 if (this.inputEl().dom.type != 'password') {
9296 var isSelectAll = false;
9298 if(this.inputEl().dom.selectionEnd > 0){
9299 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9301 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9302 event.preventDefault();
9307 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9309 event.preventDefault();
9310 // this is very hacky as keydown always get's upper case.
9312 var cc = String.fromCharCode(event.getCharCode());
9313 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9317 adjustWidth : function(tag, w){
9318 tag = tag.toLowerCase();
9319 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9320 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9324 if(tag == 'textarea'){
9327 }else if(Roo.isOpera){
9331 if(tag == 'textarea'){
9350 * @class Roo.bootstrap.TextArea
9351 * @extends Roo.bootstrap.Input
9352 * Bootstrap TextArea class
9353 * @cfg {Number} cols Specifies the visible width of a text area
9354 * @cfg {Number} rows Specifies the visible number of lines in a text area
9355 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9356 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9357 * @cfg {string} html text
9360 * Create a new TextArea
9361 * @param {Object} config The config object
9364 Roo.bootstrap.TextArea = function(config){
9365 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9369 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9379 getAutoCreate : function(){
9381 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9392 value : this.value || '',
9393 html: this.html || '',
9394 cls : 'form-control',
9395 placeholder : this.placeholder || ''
9399 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9400 input.maxLength = this.maxLength;
9404 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9408 input.cols = this.cols;
9411 if (this.readOnly) {
9412 input.readonly = true;
9416 input.name = this.name;
9420 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9424 ['xs','sm','md','lg'].map(function(size){
9425 if (settings[size]) {
9426 cfg.cls += ' col-' + size + '-' + settings[size];
9430 var inputblock = input;
9432 if(this.hasFeedback && !this.allowBlank){
9436 cls: 'glyphicon form-control-feedback'
9440 cls : 'has-feedback',
9449 if (this.before || this.after) {
9452 cls : 'input-group',
9456 inputblock.cn.push({
9458 cls : 'input-group-addon',
9463 inputblock.cn.push(input);
9465 if(this.hasFeedback && !this.allowBlank){
9466 inputblock.cls += ' has-feedback';
9467 inputblock.cn.push(feedback);
9471 inputblock.cn.push({
9473 cls : 'input-group-addon',
9480 if (align ==='left' && this.fieldLabel.length) {
9485 cls : 'control-label',
9486 html : this.fieldLabel
9497 if(this.labelWidth > 12){
9498 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9501 if(this.labelWidth < 13 && this.labelmd == 0){
9502 this.labelmd = this.labelWidth;
9505 if(this.labellg > 0){
9506 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9507 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9510 if(this.labelmd > 0){
9511 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9512 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9515 if(this.labelsm > 0){
9516 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9517 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9520 if(this.labelxs > 0){
9521 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9522 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9525 } else if ( this.fieldLabel.length) {
9530 //cls : 'input-group-addon',
9531 html : this.fieldLabel
9549 if (this.disabled) {
9550 input.disabled=true;
9557 * return the real textarea element.
9559 inputEl: function ()
9561 return this.el.select('textarea.form-control',true).first();
9565 * Clear any invalid styles/messages for this field
9567 clearInvalid : function()
9570 if(!this.el || this.preventMark){ // not rendered
9574 var label = this.el.select('label', true).first();
9575 var icon = this.el.select('i.fa-star', true).first();
9581 this.el.removeClass(this.invalidClass);
9583 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9585 var feedback = this.el.select('.form-control-feedback', true).first();
9588 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9593 this.fireEvent('valid', this);
9597 * Mark this field as valid
9599 markValid : function()
9601 if(!this.el || this.preventMark){ // not rendered
9605 this.el.removeClass([this.invalidClass, this.validClass]);
9607 var feedback = this.el.select('.form-control-feedback', true).first();
9610 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9613 if(this.disabled || this.allowBlank){
9617 var label = this.el.select('label', true).first();
9618 var icon = this.el.select('i.fa-star', true).first();
9624 this.el.addClass(this.validClass);
9626 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9628 var feedback = this.el.select('.form-control-feedback', true).first();
9631 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9632 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9637 this.fireEvent('valid', this);
9641 * Mark this field as invalid
9642 * @param {String} msg The validation message
9644 markInvalid : function(msg)
9646 if(!this.el || this.preventMark){ // not rendered
9650 this.el.removeClass([this.invalidClass, this.validClass]);
9652 var feedback = this.el.select('.form-control-feedback', true).first();
9655 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9658 if(this.disabled || this.allowBlank){
9662 var label = this.el.select('label', true).first();
9663 var icon = this.el.select('i.fa-star', true).first();
9665 if(!this.getValue().length && label && !icon){
9666 this.el.createChild({
9668 cls : 'text-danger fa fa-lg fa-star',
9669 tooltip : 'This field is required',
9670 style : 'margin-right:5px;'
9674 this.el.addClass(this.invalidClass);
9676 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9678 var feedback = this.el.select('.form-control-feedback', true).first();
9681 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9683 if(this.getValue().length || this.forceFeedback){
9684 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9691 this.fireEvent('invalid', this, msg);
9699 * trigger field - base class for combo..
9704 * @class Roo.bootstrap.TriggerField
9705 * @extends Roo.bootstrap.Input
9706 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9707 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9708 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9709 * for which you can provide a custom implementation. For example:
9711 var trigger = new Roo.bootstrap.TriggerField();
9712 trigger.onTriggerClick = myTriggerFn;
9713 trigger.applyTo('my-field');
9716 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9717 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9718 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9719 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9720 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9723 * Create a new TriggerField.
9724 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9725 * to the base TextField)
9727 Roo.bootstrap.TriggerField = function(config){
9728 this.mimicing = false;
9729 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9732 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9734 * @cfg {String} triggerClass A CSS class to apply to the trigger
9737 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9742 * @cfg {Boolean} removable (true|false) special filter default false
9746 /** @cfg {Boolean} grow @hide */
9747 /** @cfg {Number} growMin @hide */
9748 /** @cfg {Number} growMax @hide */
9754 autoSize: Roo.emptyFn,
9761 actionMode : 'wrap',
9766 getAutoCreate : function(){
9768 var align = this.labelAlign || this.parentLabelAlign();
9773 cls: 'form-group' //input-group
9780 type : this.inputType,
9781 cls : 'form-control',
9782 autocomplete: 'new-password',
9783 placeholder : this.placeholder || ''
9787 input.name = this.name;
9790 input.cls += ' input-' + this.size;
9793 if (this.disabled) {
9794 input.disabled=true;
9797 var inputblock = input;
9799 if(this.hasFeedback && !this.allowBlank){
9803 cls: 'glyphicon form-control-feedback'
9806 if(this.removable && !this.editable && !this.tickable){
9808 cls : 'has-feedback',
9814 cls : 'roo-combo-removable-btn close'
9821 cls : 'has-feedback',
9830 if(this.removable && !this.editable && !this.tickable){
9832 cls : 'roo-removable',
9838 cls : 'roo-combo-removable-btn close'
9845 if (this.before || this.after) {
9848 cls : 'input-group',
9852 inputblock.cn.push({
9854 cls : 'input-group-addon',
9859 inputblock.cn.push(input);
9861 if(this.hasFeedback && !this.allowBlank){
9862 inputblock.cls += ' has-feedback';
9863 inputblock.cn.push(feedback);
9867 inputblock.cn.push({
9869 cls : 'input-group-addon',
9882 cls: 'form-hidden-field'
9896 cls: 'form-hidden-field'
9900 cls: 'roo-select2-choices',
9904 cls: 'roo-select2-search-field',
9917 cls: 'roo-select2-container input-group',
9922 // cls: 'typeahead typeahead-long dropdown-menu',
9923 // style: 'display:none'
9928 if(!this.multiple && this.showToggleBtn){
9934 if (this.caret != false) {
9937 cls: 'fa fa-' + this.caret
9944 cls : 'input-group-addon btn dropdown-toggle',
9949 cls: 'combobox-clear',
9963 combobox.cls += ' roo-select2-container-multi';
9966 if (align ==='left' && this.fieldLabel.length) {
9968 cfg.cls += ' roo-form-group-label-left';
9973 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9974 tooltip : 'This field is required'
9979 cls : 'control-label',
9980 html : this.fieldLabel
9992 var labelCfg = cfg.cn[1];
9993 var contentCfg = cfg.cn[2];
9995 if(this.indicatorpos == 'right'){
10000 cls : 'control-label',
10004 html : this.fieldLabel
10008 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10009 tooltip : 'This field is required'
10022 labelCfg = cfg.cn[0];
10023 contentCfg = cfg.cn[1];
10026 if(this.labelWidth > 12){
10027 labelCfg.style = "width: " + this.labelWidth + 'px';
10030 if(this.labelWidth < 13 && this.labelmd == 0){
10031 this.labelmd = this.labelWidth;
10034 if(this.labellg > 0){
10035 labelCfg.cls += ' col-lg-' + this.labellg;
10036 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10039 if(this.labelmd > 0){
10040 labelCfg.cls += ' col-md-' + this.labelmd;
10041 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10044 if(this.labelsm > 0){
10045 labelCfg.cls += ' col-sm-' + this.labelsm;
10046 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10049 if(this.labelxs > 0){
10050 labelCfg.cls += ' col-xs-' + this.labelxs;
10051 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10054 } else if ( this.fieldLabel.length) {
10055 // Roo.log(" label");
10059 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10060 tooltip : 'This field is required'
10064 //cls : 'input-group-addon',
10065 html : this.fieldLabel
10073 if(this.indicatorpos == 'right'){
10081 html : this.fieldLabel
10085 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10086 tooltip : 'This field is required'
10099 // Roo.log(" no label && no align");
10106 ['xs','sm','md','lg'].map(function(size){
10107 if (settings[size]) {
10108 cfg.cls += ' col-' + size + '-' + settings[size];
10119 onResize : function(w, h){
10120 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10121 // if(typeof w == 'number'){
10122 // var x = w - this.trigger.getWidth();
10123 // this.inputEl().setWidth(this.adjustWidth('input', x));
10124 // this.trigger.setStyle('left', x+'px');
10129 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10132 getResizeEl : function(){
10133 return this.inputEl();
10137 getPositionEl : function(){
10138 return this.inputEl();
10142 alignErrorIcon : function(){
10143 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10147 initEvents : function(){
10151 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10152 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10153 if(!this.multiple && this.showToggleBtn){
10154 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10155 if(this.hideTrigger){
10156 this.trigger.setDisplayed(false);
10158 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10162 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10165 if(this.removable && !this.editable && !this.tickable){
10166 var close = this.closeTriggerEl();
10169 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10170 close.on('click', this.removeBtnClick, this, close);
10174 //this.trigger.addClassOnOver('x-form-trigger-over');
10175 //this.trigger.addClassOnClick('x-form-trigger-click');
10178 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10182 closeTriggerEl : function()
10184 var close = this.el.select('.roo-combo-removable-btn', true).first();
10185 return close ? close : false;
10188 removeBtnClick : function(e, h, el)
10190 e.preventDefault();
10192 if(this.fireEvent("remove", this) !== false){
10194 this.fireEvent("afterremove", this)
10198 createList : function()
10200 this.list = Roo.get(document.body).createChild({
10202 cls: 'typeahead typeahead-long dropdown-menu',
10203 style: 'display:none'
10206 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10211 initTrigger : function(){
10216 onDestroy : function(){
10218 this.trigger.removeAllListeners();
10219 // this.trigger.remove();
10222 // this.wrap.remove();
10224 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10228 onFocus : function(){
10229 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10231 if(!this.mimicing){
10232 this.wrap.addClass('x-trigger-wrap-focus');
10233 this.mimicing = true;
10234 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10235 if(this.monitorTab){
10236 this.el.on("keydown", this.checkTab, this);
10243 checkTab : function(e){
10244 if(e.getKey() == e.TAB){
10245 this.triggerBlur();
10250 onBlur : function(){
10255 mimicBlur : function(e, t){
10257 if(!this.wrap.contains(t) && this.validateBlur()){
10258 this.triggerBlur();
10264 triggerBlur : function(){
10265 this.mimicing = false;
10266 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10267 if(this.monitorTab){
10268 this.el.un("keydown", this.checkTab, this);
10270 //this.wrap.removeClass('x-trigger-wrap-focus');
10271 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10275 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10276 validateBlur : function(e, t){
10281 onDisable : function(){
10282 this.inputEl().dom.disabled = true;
10283 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10285 // this.wrap.addClass('x-item-disabled');
10290 onEnable : function(){
10291 this.inputEl().dom.disabled = false;
10292 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10294 // this.el.removeClass('x-item-disabled');
10299 onShow : function(){
10300 var ae = this.getActionEl();
10303 ae.dom.style.display = '';
10304 ae.dom.style.visibility = 'visible';
10310 onHide : function(){
10311 var ae = this.getActionEl();
10312 ae.dom.style.display = 'none';
10316 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10317 * by an implementing function.
10319 * @param {EventObject} e
10321 onTriggerClick : Roo.emptyFn
10325 * Ext JS Library 1.1.1
10326 * Copyright(c) 2006-2007, Ext JS, LLC.
10328 * Originally Released Under LGPL - original licence link has changed is not relivant.
10331 * <script type="text/javascript">
10336 * @class Roo.data.SortTypes
10338 * Defines the default sorting (casting?) comparison functions used when sorting data.
10340 Roo.data.SortTypes = {
10342 * Default sort that does nothing
10343 * @param {Mixed} s The value being converted
10344 * @return {Mixed} The comparison value
10346 none : function(s){
10351 * The regular expression used to strip tags
10355 stripTagsRE : /<\/?[^>]+>/gi,
10358 * Strips all HTML tags to sort on text only
10359 * @param {Mixed} s The value being converted
10360 * @return {String} The comparison value
10362 asText : function(s){
10363 return String(s).replace(this.stripTagsRE, "");
10367 * Strips all HTML tags to sort on text only - Case insensitive
10368 * @param {Mixed} s The value being converted
10369 * @return {String} The comparison value
10371 asUCText : function(s){
10372 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10376 * Case insensitive string
10377 * @param {Mixed} s The value being converted
10378 * @return {String} The comparison value
10380 asUCString : function(s) {
10381 return String(s).toUpperCase();
10386 * @param {Mixed} s The value being converted
10387 * @return {Number} The comparison value
10389 asDate : function(s) {
10393 if(s instanceof Date){
10394 return s.getTime();
10396 return Date.parse(String(s));
10401 * @param {Mixed} s The value being converted
10402 * @return {Float} The comparison value
10404 asFloat : function(s) {
10405 var val = parseFloat(String(s).replace(/,/g, ""));
10414 * @param {Mixed} s The value being converted
10415 * @return {Number} The comparison value
10417 asInt : function(s) {
10418 var val = parseInt(String(s).replace(/,/g, ""));
10426 * Ext JS Library 1.1.1
10427 * Copyright(c) 2006-2007, Ext JS, LLC.
10429 * Originally Released Under LGPL - original licence link has changed is not relivant.
10432 * <script type="text/javascript">
10436 * @class Roo.data.Record
10437 * Instances of this class encapsulate both record <em>definition</em> information, and record
10438 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10439 * to access Records cached in an {@link Roo.data.Store} object.<br>
10441 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10442 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10445 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10447 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10448 * {@link #create}. The parameters are the same.
10449 * @param {Array} data An associative Array of data values keyed by the field name.
10450 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10451 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10452 * not specified an integer id is generated.
10454 Roo.data.Record = function(data, id){
10455 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10460 * Generate a constructor for a specific record layout.
10461 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10462 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10463 * Each field definition object may contain the following properties: <ul>
10464 * <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,
10465 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10466 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10467 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10468 * is being used, then this is a string containing the javascript expression to reference the data relative to
10469 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10470 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10471 * this may be omitted.</p></li>
10472 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10473 * <ul><li>auto (Default, implies no conversion)</li>
10478 * <li>date</li></ul></p></li>
10479 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10480 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10481 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10482 * by the Reader into an object that will be stored in the Record. It is passed the
10483 * following parameters:<ul>
10484 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10486 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10488 * <br>usage:<br><pre><code>
10489 var TopicRecord = Roo.data.Record.create(
10490 {name: 'title', mapping: 'topic_title'},
10491 {name: 'author', mapping: 'username'},
10492 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10493 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10494 {name: 'lastPoster', mapping: 'user2'},
10495 {name: 'excerpt', mapping: 'post_text'}
10498 var myNewRecord = new TopicRecord({
10499 title: 'Do my job please',
10502 lastPost: new Date(),
10503 lastPoster: 'Animal',
10504 excerpt: 'No way dude!'
10506 myStore.add(myNewRecord);
10511 Roo.data.Record.create = function(o){
10512 var f = function(){
10513 f.superclass.constructor.apply(this, arguments);
10515 Roo.extend(f, Roo.data.Record);
10516 var p = f.prototype;
10517 p.fields = new Roo.util.MixedCollection(false, function(field){
10520 for(var i = 0, len = o.length; i < len; i++){
10521 p.fields.add(new Roo.data.Field(o[i]));
10523 f.getField = function(name){
10524 return p.fields.get(name);
10529 Roo.data.Record.AUTO_ID = 1000;
10530 Roo.data.Record.EDIT = 'edit';
10531 Roo.data.Record.REJECT = 'reject';
10532 Roo.data.Record.COMMIT = 'commit';
10534 Roo.data.Record.prototype = {
10536 * Readonly flag - true if this record has been modified.
10545 join : function(store){
10546 this.store = store;
10550 * Set the named field to the specified value.
10551 * @param {String} name The name of the field to set.
10552 * @param {Object} value The value to set the field to.
10554 set : function(name, value){
10555 if(this.data[name] == value){
10559 if(!this.modified){
10560 this.modified = {};
10562 if(typeof this.modified[name] == 'undefined'){
10563 this.modified[name] = this.data[name];
10565 this.data[name] = value;
10566 if(!this.editing && this.store){
10567 this.store.afterEdit(this);
10572 * Get the value of the named field.
10573 * @param {String} name The name of the field to get the value of.
10574 * @return {Object} The value of the field.
10576 get : function(name){
10577 return this.data[name];
10581 beginEdit : function(){
10582 this.editing = true;
10583 this.modified = {};
10587 cancelEdit : function(){
10588 this.editing = false;
10589 delete this.modified;
10593 endEdit : function(){
10594 this.editing = false;
10595 if(this.dirty && this.store){
10596 this.store.afterEdit(this);
10601 * Usually called by the {@link Roo.data.Store} which owns the Record.
10602 * Rejects all changes made to the Record since either creation, or the last commit operation.
10603 * Modified fields are reverted to their original values.
10605 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10606 * of reject operations.
10608 reject : function(){
10609 var m = this.modified;
10611 if(typeof m[n] != "function"){
10612 this.data[n] = m[n];
10615 this.dirty = false;
10616 delete this.modified;
10617 this.editing = false;
10619 this.store.afterReject(this);
10624 * Usually called by the {@link Roo.data.Store} which owns the Record.
10625 * Commits all changes made to the Record since either creation, or the last commit operation.
10627 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10628 * of commit operations.
10630 commit : function(){
10631 this.dirty = false;
10632 delete this.modified;
10633 this.editing = false;
10635 this.store.afterCommit(this);
10640 hasError : function(){
10641 return this.error != null;
10645 clearError : function(){
10650 * Creates a copy of this record.
10651 * @param {String} id (optional) A new record id if you don't want to use this record's id
10654 copy : function(newId) {
10655 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10659 * Ext JS Library 1.1.1
10660 * Copyright(c) 2006-2007, Ext JS, LLC.
10662 * Originally Released Under LGPL - original licence link has changed is not relivant.
10665 * <script type="text/javascript">
10671 * @class Roo.data.Store
10672 * @extends Roo.util.Observable
10673 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10674 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10676 * 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
10677 * has no knowledge of the format of the data returned by the Proxy.<br>
10679 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10680 * instances from the data object. These records are cached and made available through accessor functions.
10682 * Creates a new Store.
10683 * @param {Object} config A config object containing the objects needed for the Store to access data,
10684 * and read the data into Records.
10686 Roo.data.Store = function(config){
10687 this.data = new Roo.util.MixedCollection(false);
10688 this.data.getKey = function(o){
10691 this.baseParams = {};
10693 this.paramNames = {
10698 "multisort" : "_multisort"
10701 if(config && config.data){
10702 this.inlineData = config.data;
10703 delete config.data;
10706 Roo.apply(this, config);
10708 if(this.reader){ // reader passed
10709 this.reader = Roo.factory(this.reader, Roo.data);
10710 this.reader.xmodule = this.xmodule || false;
10711 if(!this.recordType){
10712 this.recordType = this.reader.recordType;
10714 if(this.reader.onMetaChange){
10715 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10719 if(this.recordType){
10720 this.fields = this.recordType.prototype.fields;
10722 this.modified = [];
10726 * @event datachanged
10727 * Fires when the data cache has changed, and a widget which is using this Store
10728 * as a Record cache should refresh its view.
10729 * @param {Store} this
10731 datachanged : true,
10733 * @event metachange
10734 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10735 * @param {Store} this
10736 * @param {Object} meta The JSON metadata
10741 * Fires when Records have been added to the Store
10742 * @param {Store} this
10743 * @param {Roo.data.Record[]} records The array of Records added
10744 * @param {Number} index The index at which the record(s) were added
10749 * Fires when a Record has been removed from the Store
10750 * @param {Store} this
10751 * @param {Roo.data.Record} record The Record that was removed
10752 * @param {Number} index The index at which the record was removed
10757 * Fires when a Record has been updated
10758 * @param {Store} this
10759 * @param {Roo.data.Record} record The Record that was updated
10760 * @param {String} operation The update operation being performed. Value may be one of:
10762 Roo.data.Record.EDIT
10763 Roo.data.Record.REJECT
10764 Roo.data.Record.COMMIT
10770 * Fires when the data cache has been cleared.
10771 * @param {Store} this
10775 * @event beforeload
10776 * Fires before a request is made for a new data object. If the beforeload handler returns false
10777 * the load action will be canceled.
10778 * @param {Store} this
10779 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10783 * @event beforeloadadd
10784 * Fires after a new set of Records has been loaded.
10785 * @param {Store} this
10786 * @param {Roo.data.Record[]} records The Records that were loaded
10787 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10789 beforeloadadd : true,
10792 * Fires after a new set of Records has been loaded, before they are added to the store.
10793 * @param {Store} this
10794 * @param {Roo.data.Record[]} records The Records that were loaded
10795 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10796 * @params {Object} return from reader
10800 * @event loadexception
10801 * Fires if an exception occurs in the Proxy during loading.
10802 * Called with the signature of the Proxy's "loadexception" event.
10803 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10806 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10807 * @param {Object} load options
10808 * @param {Object} jsonData from your request (normally this contains the Exception)
10810 loadexception : true
10814 this.proxy = Roo.factory(this.proxy, Roo.data);
10815 this.proxy.xmodule = this.xmodule || false;
10816 this.relayEvents(this.proxy, ["loadexception"]);
10818 this.sortToggle = {};
10819 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10821 Roo.data.Store.superclass.constructor.call(this);
10823 if(this.inlineData){
10824 this.loadData(this.inlineData);
10825 delete this.inlineData;
10829 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10831 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10832 * without a remote query - used by combo/forms at present.
10836 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10839 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10842 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10843 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10846 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10847 * on any HTTP request
10850 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10853 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10857 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10858 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10860 remoteSort : false,
10863 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10864 * loaded or when a record is removed. (defaults to false).
10866 pruneModifiedRecords : false,
10869 lastOptions : null,
10872 * Add Records to the Store and fires the add event.
10873 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10875 add : function(records){
10876 records = [].concat(records);
10877 for(var i = 0, len = records.length; i < len; i++){
10878 records[i].join(this);
10880 var index = this.data.length;
10881 this.data.addAll(records);
10882 this.fireEvent("add", this, records, index);
10886 * Remove a Record from the Store and fires the remove event.
10887 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10889 remove : function(record){
10890 var index = this.data.indexOf(record);
10891 this.data.removeAt(index);
10892 if(this.pruneModifiedRecords){
10893 this.modified.remove(record);
10895 this.fireEvent("remove", this, record, index);
10899 * Remove all Records from the Store and fires the clear event.
10901 removeAll : function(){
10903 if(this.pruneModifiedRecords){
10904 this.modified = [];
10906 this.fireEvent("clear", this);
10910 * Inserts Records to the Store at the given index and fires the add event.
10911 * @param {Number} index The start index at which to insert the passed Records.
10912 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10914 insert : function(index, records){
10915 records = [].concat(records);
10916 for(var i = 0, len = records.length; i < len; i++){
10917 this.data.insert(index, records[i]);
10918 records[i].join(this);
10920 this.fireEvent("add", this, records, index);
10924 * Get the index within the cache of the passed Record.
10925 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10926 * @return {Number} The index of the passed Record. Returns -1 if not found.
10928 indexOf : function(record){
10929 return this.data.indexOf(record);
10933 * Get the index within the cache of the Record with the passed id.
10934 * @param {String} id The id of the Record to find.
10935 * @return {Number} The index of the Record. Returns -1 if not found.
10937 indexOfId : function(id){
10938 return this.data.indexOfKey(id);
10942 * Get the Record with the specified id.
10943 * @param {String} id The id of the Record to find.
10944 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10946 getById : function(id){
10947 return this.data.key(id);
10951 * Get the Record at the specified index.
10952 * @param {Number} index The index of the Record to find.
10953 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10955 getAt : function(index){
10956 return this.data.itemAt(index);
10960 * Returns a range of Records between specified indices.
10961 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10962 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10963 * @return {Roo.data.Record[]} An array of Records
10965 getRange : function(start, end){
10966 return this.data.getRange(start, end);
10970 storeOptions : function(o){
10971 o = Roo.apply({}, o);
10974 this.lastOptions = o;
10978 * Loads the Record cache from the configured Proxy using the configured Reader.
10980 * If using remote paging, then the first load call must specify the <em>start</em>
10981 * and <em>limit</em> properties in the options.params property to establish the initial
10982 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10984 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10985 * and this call will return before the new data has been loaded. Perform any post-processing
10986 * in a callback function, or in a "load" event handler.</strong>
10988 * @param {Object} options An object containing properties which control loading options:<ul>
10989 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10990 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10991 * passed the following arguments:<ul>
10992 * <li>r : Roo.data.Record[]</li>
10993 * <li>options: Options object from the load call</li>
10994 * <li>success: Boolean success indicator</li></ul></li>
10995 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10996 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10999 load : function(options){
11000 options = options || {};
11001 if(this.fireEvent("beforeload", this, options) !== false){
11002 this.storeOptions(options);
11003 var p = Roo.apply(options.params || {}, this.baseParams);
11004 // if meta was not loaded from remote source.. try requesting it.
11005 if (!this.reader.metaFromRemote) {
11006 p._requestMeta = 1;
11008 if(this.sortInfo && this.remoteSort){
11009 var pn = this.paramNames;
11010 p[pn["sort"]] = this.sortInfo.field;
11011 p[pn["dir"]] = this.sortInfo.direction;
11013 if (this.multiSort) {
11014 var pn = this.paramNames;
11015 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11018 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11023 * Reloads the Record cache from the configured Proxy using the configured Reader and
11024 * the options from the last load operation performed.
11025 * @param {Object} options (optional) An object containing properties which may override the options
11026 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11027 * the most recently used options are reused).
11029 reload : function(options){
11030 this.load(Roo.applyIf(options||{}, this.lastOptions));
11034 // Called as a callback by the Reader during a load operation.
11035 loadRecords : function(o, options, success){
11036 if(!o || success === false){
11037 if(success !== false){
11038 this.fireEvent("load", this, [], options, o);
11040 if(options.callback){
11041 options.callback.call(options.scope || this, [], options, false);
11045 // if data returned failure - throw an exception.
11046 if (o.success === false) {
11047 // show a message if no listener is registered.
11048 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11049 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11051 // loadmask wil be hooked into this..
11052 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11055 var r = o.records, t = o.totalRecords || r.length;
11057 this.fireEvent("beforeloadadd", this, r, options, o);
11059 if(!options || options.add !== true){
11060 if(this.pruneModifiedRecords){
11061 this.modified = [];
11063 for(var i = 0, len = r.length; i < len; i++){
11067 this.data = this.snapshot;
11068 delete this.snapshot;
11071 this.data.addAll(r);
11072 this.totalLength = t;
11074 this.fireEvent("datachanged", this);
11076 this.totalLength = Math.max(t, this.data.length+r.length);
11080 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11082 var e = new Roo.data.Record({});
11084 e.set(this.parent.displayField, this.parent.emptyTitle);
11085 e.set(this.parent.valueField, '');
11090 this.fireEvent("load", this, r, options, o);
11091 if(options.callback){
11092 options.callback.call(options.scope || this, r, options, true);
11098 * Loads data from a passed data block. A Reader which understands the format of the data
11099 * must have been configured in the constructor.
11100 * @param {Object} data The data block from which to read the Records. The format of the data expected
11101 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11102 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11104 loadData : function(o, append){
11105 var r = this.reader.readRecords(o);
11106 this.loadRecords(r, {add: append}, true);
11110 * Gets the number of cached records.
11112 * <em>If using paging, this may not be the total size of the dataset. If the data object
11113 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11114 * the data set size</em>
11116 getCount : function(){
11117 return this.data.length || 0;
11121 * Gets the total number of records in the dataset as returned by the server.
11123 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11124 * the dataset size</em>
11126 getTotalCount : function(){
11127 return this.totalLength || 0;
11131 * Returns the sort state of the Store as an object with two properties:
11133 field {String} The name of the field by which the Records are sorted
11134 direction {String} The sort order, "ASC" or "DESC"
11137 getSortState : function(){
11138 return this.sortInfo;
11142 applySort : function(){
11143 if(this.sortInfo && !this.remoteSort){
11144 var s = this.sortInfo, f = s.field;
11145 var st = this.fields.get(f).sortType;
11146 var fn = function(r1, r2){
11147 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11148 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11150 this.data.sort(s.direction, fn);
11151 if(this.snapshot && this.snapshot != this.data){
11152 this.snapshot.sort(s.direction, fn);
11158 * Sets the default sort column and order to be used by the next load operation.
11159 * @param {String} fieldName The name of the field to sort by.
11160 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11162 setDefaultSort : function(field, dir){
11163 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11167 * Sort the Records.
11168 * If remote sorting is used, the sort is performed on the server, and the cache is
11169 * reloaded. If local sorting is used, the cache is sorted internally.
11170 * @param {String} fieldName The name of the field to sort by.
11171 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11173 sort : function(fieldName, dir){
11174 var f = this.fields.get(fieldName);
11176 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11178 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11179 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11184 this.sortToggle[f.name] = dir;
11185 this.sortInfo = {field: f.name, direction: dir};
11186 if(!this.remoteSort){
11188 this.fireEvent("datachanged", this);
11190 this.load(this.lastOptions);
11195 * Calls the specified function for each of the Records in the cache.
11196 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11197 * Returning <em>false</em> aborts and exits the iteration.
11198 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11200 each : function(fn, scope){
11201 this.data.each(fn, scope);
11205 * Gets all records modified since the last commit. Modified records are persisted across load operations
11206 * (e.g., during paging).
11207 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11209 getModifiedRecords : function(){
11210 return this.modified;
11214 createFilterFn : function(property, value, anyMatch){
11215 if(!value.exec){ // not a regex
11216 value = String(value);
11217 if(value.length == 0){
11220 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11222 return function(r){
11223 return value.test(r.data[property]);
11228 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11229 * @param {String} property A field on your records
11230 * @param {Number} start The record index to start at (defaults to 0)
11231 * @param {Number} end The last record index to include (defaults to length - 1)
11232 * @return {Number} The sum
11234 sum : function(property, start, end){
11235 var rs = this.data.items, v = 0;
11236 start = start || 0;
11237 end = (end || end === 0) ? end : rs.length-1;
11239 for(var i = start; i <= end; i++){
11240 v += (rs[i].data[property] || 0);
11246 * Filter the records by a specified property.
11247 * @param {String} field A field on your records
11248 * @param {String/RegExp} value Either a string that the field
11249 * should start with or a RegExp to test against the field
11250 * @param {Boolean} anyMatch True to match any part not just the beginning
11252 filter : function(property, value, anyMatch){
11253 var fn = this.createFilterFn(property, value, anyMatch);
11254 return fn ? this.filterBy(fn) : this.clearFilter();
11258 * Filter by a function. The specified function will be called with each
11259 * record in this data source. If the function returns true the record is included,
11260 * otherwise it is filtered.
11261 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11262 * @param {Object} scope (optional) The scope of the function (defaults to this)
11264 filterBy : function(fn, scope){
11265 this.snapshot = this.snapshot || this.data;
11266 this.data = this.queryBy(fn, scope||this);
11267 this.fireEvent("datachanged", this);
11271 * Query the records by a specified property.
11272 * @param {String} field A field on your records
11273 * @param {String/RegExp} value Either a string that the field
11274 * should start with or a RegExp to test against the field
11275 * @param {Boolean} anyMatch True to match any part not just the beginning
11276 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11278 query : function(property, value, anyMatch){
11279 var fn = this.createFilterFn(property, value, anyMatch);
11280 return fn ? this.queryBy(fn) : this.data.clone();
11284 * Query by a function. The specified function will be called with each
11285 * record in this data source. If the function returns true the record is included
11287 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11288 * @param {Object} scope (optional) The scope of the function (defaults to this)
11289 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11291 queryBy : function(fn, scope){
11292 var data = this.snapshot || this.data;
11293 return data.filterBy(fn, scope||this);
11297 * Collects unique values for a particular dataIndex from this store.
11298 * @param {String} dataIndex The property to collect
11299 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11300 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11301 * @return {Array} An array of the unique values
11303 collect : function(dataIndex, allowNull, bypassFilter){
11304 var d = (bypassFilter === true && this.snapshot) ?
11305 this.snapshot.items : this.data.items;
11306 var v, sv, r = [], l = {};
11307 for(var i = 0, len = d.length; i < len; i++){
11308 v = d[i].data[dataIndex];
11310 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11319 * Revert to a view of the Record cache with no filtering applied.
11320 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11322 clearFilter : function(suppressEvent){
11323 if(this.snapshot && this.snapshot != this.data){
11324 this.data = this.snapshot;
11325 delete this.snapshot;
11326 if(suppressEvent !== true){
11327 this.fireEvent("datachanged", this);
11333 afterEdit : function(record){
11334 if(this.modified.indexOf(record) == -1){
11335 this.modified.push(record);
11337 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11341 afterReject : function(record){
11342 this.modified.remove(record);
11343 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11347 afterCommit : function(record){
11348 this.modified.remove(record);
11349 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11353 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11354 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11356 commitChanges : function(){
11357 var m = this.modified.slice(0);
11358 this.modified = [];
11359 for(var i = 0, len = m.length; i < len; i++){
11365 * Cancel outstanding changes on all changed records.
11367 rejectChanges : function(){
11368 var m = this.modified.slice(0);
11369 this.modified = [];
11370 for(var i = 0, len = m.length; i < len; i++){
11375 onMetaChange : function(meta, rtype, o){
11376 this.recordType = rtype;
11377 this.fields = rtype.prototype.fields;
11378 delete this.snapshot;
11379 this.sortInfo = meta.sortInfo || this.sortInfo;
11380 this.modified = [];
11381 this.fireEvent('metachange', this, this.reader.meta);
11384 moveIndex : function(data, type)
11386 var index = this.indexOf(data);
11388 var newIndex = index + type;
11392 this.insert(newIndex, data);
11397 * Ext JS Library 1.1.1
11398 * Copyright(c) 2006-2007, Ext JS, LLC.
11400 * Originally Released Under LGPL - original licence link has changed is not relivant.
11403 * <script type="text/javascript">
11407 * @class Roo.data.SimpleStore
11408 * @extends Roo.data.Store
11409 * Small helper class to make creating Stores from Array data easier.
11410 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11411 * @cfg {Array} fields An array of field definition objects, or field name strings.
11412 * @cfg {Array} data The multi-dimensional array of data
11414 * @param {Object} config
11416 Roo.data.SimpleStore = function(config){
11417 Roo.data.SimpleStore.superclass.constructor.call(this, {
11419 reader: new Roo.data.ArrayReader({
11422 Roo.data.Record.create(config.fields)
11424 proxy : new Roo.data.MemoryProxy(config.data)
11428 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11430 * Ext JS Library 1.1.1
11431 * Copyright(c) 2006-2007, Ext JS, LLC.
11433 * Originally Released Under LGPL - original licence link has changed is not relivant.
11436 * <script type="text/javascript">
11441 * @extends Roo.data.Store
11442 * @class Roo.data.JsonStore
11443 * Small helper class to make creating Stores for JSON data easier. <br/>
11445 var store = new Roo.data.JsonStore({
11446 url: 'get-images.php',
11448 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11451 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11452 * JsonReader and HttpProxy (unless inline data is provided).</b>
11453 * @cfg {Array} fields An array of field definition objects, or field name strings.
11455 * @param {Object} config
11457 Roo.data.JsonStore = function(c){
11458 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11459 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11460 reader: new Roo.data.JsonReader(c, c.fields)
11463 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11465 * Ext JS Library 1.1.1
11466 * Copyright(c) 2006-2007, Ext JS, LLC.
11468 * Originally Released Under LGPL - original licence link has changed is not relivant.
11471 * <script type="text/javascript">
11475 Roo.data.Field = function(config){
11476 if(typeof config == "string"){
11477 config = {name: config};
11479 Roo.apply(this, config);
11482 this.type = "auto";
11485 var st = Roo.data.SortTypes;
11486 // named sortTypes are supported, here we look them up
11487 if(typeof this.sortType == "string"){
11488 this.sortType = st[this.sortType];
11491 // set default sortType for strings and dates
11492 if(!this.sortType){
11495 this.sortType = st.asUCString;
11498 this.sortType = st.asDate;
11501 this.sortType = st.none;
11506 var stripRe = /[\$,%]/g;
11508 // prebuilt conversion function for this field, instead of
11509 // switching every time we're reading a value
11511 var cv, dateFormat = this.dateFormat;
11516 cv = function(v){ return v; };
11519 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11523 return v !== undefined && v !== null && v !== '' ?
11524 parseInt(String(v).replace(stripRe, ""), 10) : '';
11529 return v !== undefined && v !== null && v !== '' ?
11530 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11535 cv = function(v){ return v === true || v === "true" || v == 1; };
11542 if(v instanceof Date){
11546 if(dateFormat == "timestamp"){
11547 return new Date(v*1000);
11549 return Date.parseDate(v, dateFormat);
11551 var parsed = Date.parse(v);
11552 return parsed ? new Date(parsed) : null;
11561 Roo.data.Field.prototype = {
11569 * Ext JS Library 1.1.1
11570 * Copyright(c) 2006-2007, Ext JS, LLC.
11572 * Originally Released Under LGPL - original licence link has changed is not relivant.
11575 * <script type="text/javascript">
11578 // Base class for reading structured data from a data source. This class is intended to be
11579 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11582 * @class Roo.data.DataReader
11583 * Base class for reading structured data from a data source. This class is intended to be
11584 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11587 Roo.data.DataReader = function(meta, recordType){
11591 this.recordType = recordType instanceof Array ?
11592 Roo.data.Record.create(recordType) : recordType;
11595 Roo.data.DataReader.prototype = {
11597 * Create an empty record
11598 * @param {Object} data (optional) - overlay some values
11599 * @return {Roo.data.Record} record created.
11601 newRow : function(d) {
11603 this.recordType.prototype.fields.each(function(c) {
11605 case 'int' : da[c.name] = 0; break;
11606 case 'date' : da[c.name] = new Date(); break;
11607 case 'float' : da[c.name] = 0.0; break;
11608 case 'boolean' : da[c.name] = false; break;
11609 default : da[c.name] = ""; break;
11613 return new this.recordType(Roo.apply(da, d));
11618 * Ext JS Library 1.1.1
11619 * Copyright(c) 2006-2007, Ext JS, LLC.
11621 * Originally Released Under LGPL - original licence link has changed is not relivant.
11624 * <script type="text/javascript">
11628 * @class Roo.data.DataProxy
11629 * @extends Roo.data.Observable
11630 * This class is an abstract base class for implementations which provide retrieval of
11631 * unformatted data objects.<br>
11633 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11634 * (of the appropriate type which knows how to parse the data object) to provide a block of
11635 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11637 * Custom implementations must implement the load method as described in
11638 * {@link Roo.data.HttpProxy#load}.
11640 Roo.data.DataProxy = function(){
11643 * @event beforeload
11644 * Fires before a network request is made to retrieve a data object.
11645 * @param {Object} This DataProxy object.
11646 * @param {Object} params The params parameter to the load function.
11651 * Fires before the load method's callback is called.
11652 * @param {Object} This DataProxy object.
11653 * @param {Object} o The data object.
11654 * @param {Object} arg The callback argument object passed to the load function.
11658 * @event loadexception
11659 * Fires if an Exception occurs during data retrieval.
11660 * @param {Object} This DataProxy object.
11661 * @param {Object} o The data object.
11662 * @param {Object} arg The callback argument object passed to the load function.
11663 * @param {Object} e The Exception.
11665 loadexception : true
11667 Roo.data.DataProxy.superclass.constructor.call(this);
11670 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11673 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11677 * Ext JS Library 1.1.1
11678 * Copyright(c) 2006-2007, Ext JS, LLC.
11680 * Originally Released Under LGPL - original licence link has changed is not relivant.
11683 * <script type="text/javascript">
11686 * @class Roo.data.MemoryProxy
11687 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11688 * to the Reader when its load method is called.
11690 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11692 Roo.data.MemoryProxy = function(data){
11696 Roo.data.MemoryProxy.superclass.constructor.call(this);
11700 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11703 * Load data from the requested source (in this case an in-memory
11704 * data object passed to the constructor), read the data object into
11705 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11706 * process that block using the passed callback.
11707 * @param {Object} params This parameter is not used by the MemoryProxy class.
11708 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11709 * object into a block of Roo.data.Records.
11710 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11711 * The function must be passed <ul>
11712 * <li>The Record block object</li>
11713 * <li>The "arg" argument from the load function</li>
11714 * <li>A boolean success indicator</li>
11716 * @param {Object} scope The scope in which to call the callback
11717 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11719 load : function(params, reader, callback, scope, arg){
11720 params = params || {};
11723 result = reader.readRecords(this.data);
11725 this.fireEvent("loadexception", this, arg, null, e);
11726 callback.call(scope, null, arg, false);
11729 callback.call(scope, result, arg, true);
11733 update : function(params, records){
11738 * Ext JS Library 1.1.1
11739 * Copyright(c) 2006-2007, Ext JS, LLC.
11741 * Originally Released Under LGPL - original licence link has changed is not relivant.
11744 * <script type="text/javascript">
11747 * @class Roo.data.HttpProxy
11748 * @extends Roo.data.DataProxy
11749 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11750 * configured to reference a certain URL.<br><br>
11752 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11753 * from which the running page was served.<br><br>
11755 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11757 * Be aware that to enable the browser to parse an XML document, the server must set
11758 * the Content-Type header in the HTTP response to "text/xml".
11760 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11761 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11762 * will be used to make the request.
11764 Roo.data.HttpProxy = function(conn){
11765 Roo.data.HttpProxy.superclass.constructor.call(this);
11766 // is conn a conn config or a real conn?
11768 this.useAjax = !conn || !conn.events;
11772 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11773 // thse are take from connection...
11776 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11779 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11780 * extra parameters to each request made by this object. (defaults to undefined)
11783 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11784 * to each request made by this object. (defaults to undefined)
11787 * @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)
11790 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11793 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11799 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11803 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11804 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11805 * a finer-grained basis than the DataProxy events.
11807 getConnection : function(){
11808 return this.useAjax ? Roo.Ajax : this.conn;
11812 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11813 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11814 * process that block using the passed callback.
11815 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11816 * for the request to the remote server.
11817 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11818 * object into a block of Roo.data.Records.
11819 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11820 * The function must be passed <ul>
11821 * <li>The Record block object</li>
11822 * <li>The "arg" argument from the load function</li>
11823 * <li>A boolean success indicator</li>
11825 * @param {Object} scope The scope in which to call the callback
11826 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11828 load : function(params, reader, callback, scope, arg){
11829 if(this.fireEvent("beforeload", this, params) !== false){
11831 params : params || {},
11833 callback : callback,
11838 callback : this.loadResponse,
11842 Roo.applyIf(o, this.conn);
11843 if(this.activeRequest){
11844 Roo.Ajax.abort(this.activeRequest);
11846 this.activeRequest = Roo.Ajax.request(o);
11848 this.conn.request(o);
11851 callback.call(scope||this, null, arg, false);
11856 loadResponse : function(o, success, response){
11857 delete this.activeRequest;
11859 this.fireEvent("loadexception", this, o, response);
11860 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11865 result = o.reader.read(response);
11867 this.fireEvent("loadexception", this, o, response, e);
11868 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11872 this.fireEvent("load", this, o, o.request.arg);
11873 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11877 update : function(dataSet){
11882 updateResponse : function(dataSet){
11887 * Ext JS Library 1.1.1
11888 * Copyright(c) 2006-2007, Ext JS, LLC.
11890 * Originally Released Under LGPL - original licence link has changed is not relivant.
11893 * <script type="text/javascript">
11897 * @class Roo.data.ScriptTagProxy
11898 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11899 * other than the originating domain of the running page.<br><br>
11901 * <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
11902 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11904 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11905 * source code that is used as the source inside a <script> tag.<br><br>
11907 * In order for the browser to process the returned data, the server must wrap the data object
11908 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11909 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11910 * depending on whether the callback name was passed:
11913 boolean scriptTag = false;
11914 String cb = request.getParameter("callback");
11917 response.setContentType("text/javascript");
11919 response.setContentType("application/x-json");
11921 Writer out = response.getWriter();
11923 out.write(cb + "(");
11925 out.print(dataBlock.toJsonString());
11932 * @param {Object} config A configuration object.
11934 Roo.data.ScriptTagProxy = function(config){
11935 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11936 Roo.apply(this, config);
11937 this.head = document.getElementsByTagName("head")[0];
11940 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11942 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11944 * @cfg {String} url The URL from which to request the data object.
11947 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11951 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11952 * the server the name of the callback function set up by the load call to process the returned data object.
11953 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11954 * javascript output which calls this named function passing the data object as its only parameter.
11956 callbackParam : "callback",
11958 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11959 * name to the request.
11964 * Load data from the configured URL, read the data object into
11965 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11966 * process that block using the passed callback.
11967 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11968 * for the request to the remote server.
11969 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11970 * object into a block of Roo.data.Records.
11971 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11972 * The function must be passed <ul>
11973 * <li>The Record block object</li>
11974 * <li>The "arg" argument from the load function</li>
11975 * <li>A boolean success indicator</li>
11977 * @param {Object} scope The scope in which to call the callback
11978 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11980 load : function(params, reader, callback, scope, arg){
11981 if(this.fireEvent("beforeload", this, params) !== false){
11983 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11985 var url = this.url;
11986 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11988 url += "&_dc=" + (new Date().getTime());
11990 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11993 cb : "stcCallback"+transId,
11994 scriptId : "stcScript"+transId,
11998 callback : callback,
12004 window[trans.cb] = function(o){
12005 conn.handleResponse(o, trans);
12008 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12010 if(this.autoAbort !== false){
12014 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12016 var script = document.createElement("script");
12017 script.setAttribute("src", url);
12018 script.setAttribute("type", "text/javascript");
12019 script.setAttribute("id", trans.scriptId);
12020 this.head.appendChild(script);
12022 this.trans = trans;
12024 callback.call(scope||this, null, arg, false);
12029 isLoading : function(){
12030 return this.trans ? true : false;
12034 * Abort the current server request.
12036 abort : function(){
12037 if(this.isLoading()){
12038 this.destroyTrans(this.trans);
12043 destroyTrans : function(trans, isLoaded){
12044 this.head.removeChild(document.getElementById(trans.scriptId));
12045 clearTimeout(trans.timeoutId);
12047 window[trans.cb] = undefined;
12049 delete window[trans.cb];
12052 // if hasn't been loaded, wait for load to remove it to prevent script error
12053 window[trans.cb] = function(){
12054 window[trans.cb] = undefined;
12056 delete window[trans.cb];
12063 handleResponse : function(o, trans){
12064 this.trans = false;
12065 this.destroyTrans(trans, true);
12068 result = trans.reader.readRecords(o);
12070 this.fireEvent("loadexception", this, o, trans.arg, e);
12071 trans.callback.call(trans.scope||window, null, trans.arg, false);
12074 this.fireEvent("load", this, o, trans.arg);
12075 trans.callback.call(trans.scope||window, result, trans.arg, true);
12079 handleFailure : function(trans){
12080 this.trans = false;
12081 this.destroyTrans(trans, false);
12082 this.fireEvent("loadexception", this, null, trans.arg);
12083 trans.callback.call(trans.scope||window, null, trans.arg, false);
12087 * Ext JS Library 1.1.1
12088 * Copyright(c) 2006-2007, Ext JS, LLC.
12090 * Originally Released Under LGPL - original licence link has changed is not relivant.
12093 * <script type="text/javascript">
12097 * @class Roo.data.JsonReader
12098 * @extends Roo.data.DataReader
12099 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12100 * based on mappings in a provided Roo.data.Record constructor.
12102 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12103 * in the reply previously.
12108 var RecordDef = Roo.data.Record.create([
12109 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12110 {name: 'occupation'} // This field will use "occupation" as the mapping.
12112 var myReader = new Roo.data.JsonReader({
12113 totalProperty: "results", // The property which contains the total dataset size (optional)
12114 root: "rows", // The property which contains an Array of row objects
12115 id: "id" // The property within each row object that provides an ID for the record (optional)
12119 * This would consume a JSON file like this:
12121 { 'results': 2, 'rows': [
12122 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12123 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12126 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12127 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12128 * paged from the remote server.
12129 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12130 * @cfg {String} root name of the property which contains the Array of row objects.
12131 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12132 * @cfg {Array} fields Array of field definition objects
12134 * Create a new JsonReader
12135 * @param {Object} meta Metadata configuration options
12136 * @param {Object} recordType Either an Array of field definition objects,
12137 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12139 Roo.data.JsonReader = function(meta, recordType){
12142 // set some defaults:
12143 Roo.applyIf(meta, {
12144 totalProperty: 'total',
12145 successProperty : 'success',
12150 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12152 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12155 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12156 * Used by Store query builder to append _requestMeta to params.
12159 metaFromRemote : false,
12161 * This method is only used by a DataProxy which has retrieved data from a remote server.
12162 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12163 * @return {Object} data A data block which is used by an Roo.data.Store object as
12164 * a cache of Roo.data.Records.
12166 read : function(response){
12167 var json = response.responseText;
12169 var o = /* eval:var:o */ eval("("+json+")");
12171 throw {message: "JsonReader.read: Json object not found"};
12177 this.metaFromRemote = true;
12178 this.meta = o.metaData;
12179 this.recordType = Roo.data.Record.create(o.metaData.fields);
12180 this.onMetaChange(this.meta, this.recordType, o);
12182 return this.readRecords(o);
12185 // private function a store will implement
12186 onMetaChange : function(meta, recordType, o){
12193 simpleAccess: function(obj, subsc) {
12200 getJsonAccessor: function(){
12202 return function(expr) {
12204 return(re.test(expr))
12205 ? new Function("obj", "return obj." + expr)
12210 return Roo.emptyFn;
12215 * Create a data block containing Roo.data.Records from an XML document.
12216 * @param {Object} o An object which contains an Array of row objects in the property specified
12217 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12218 * which contains the total size of the dataset.
12219 * @return {Object} data A data block which is used by an Roo.data.Store object as
12220 * a cache of Roo.data.Records.
12222 readRecords : function(o){
12224 * After any data loads, the raw JSON data is available for further custom processing.
12228 var s = this.meta, Record = this.recordType,
12229 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12231 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12233 if(s.totalProperty) {
12234 this.getTotal = this.getJsonAccessor(s.totalProperty);
12236 if(s.successProperty) {
12237 this.getSuccess = this.getJsonAccessor(s.successProperty);
12239 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12241 var g = this.getJsonAccessor(s.id);
12242 this.getId = function(rec) {
12244 return (r === undefined || r === "") ? null : r;
12247 this.getId = function(){return null;};
12250 for(var jj = 0; jj < fl; jj++){
12252 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12253 this.ef[jj] = this.getJsonAccessor(map);
12257 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12258 if(s.totalProperty){
12259 var vt = parseInt(this.getTotal(o), 10);
12264 if(s.successProperty){
12265 var vs = this.getSuccess(o);
12266 if(vs === false || vs === 'false'){
12271 for(var i = 0; i < c; i++){
12274 var id = this.getId(n);
12275 for(var j = 0; j < fl; j++){
12277 var v = this.ef[j](n);
12279 Roo.log('missing convert for ' + f.name);
12283 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12285 var record = new Record(values, id);
12287 records[i] = record;
12293 totalRecords : totalRecords
12298 * Ext JS Library 1.1.1
12299 * Copyright(c) 2006-2007, Ext JS, LLC.
12301 * Originally Released Under LGPL - original licence link has changed is not relivant.
12304 * <script type="text/javascript">
12308 * @class Roo.data.ArrayReader
12309 * @extends Roo.data.DataReader
12310 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12311 * Each element of that Array represents a row of data fields. The
12312 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12313 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12317 var RecordDef = Roo.data.Record.create([
12318 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12319 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12321 var myReader = new Roo.data.ArrayReader({
12322 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12326 * This would consume an Array like this:
12328 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12330 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12332 * Create a new JsonReader
12333 * @param {Object} meta Metadata configuration options.
12334 * @param {Object} recordType Either an Array of field definition objects
12335 * as specified to {@link Roo.data.Record#create},
12336 * or an {@link Roo.data.Record} object
12337 * created using {@link Roo.data.Record#create}.
12339 Roo.data.ArrayReader = function(meta, recordType){
12340 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12343 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12345 * Create a data block containing Roo.data.Records from an XML document.
12346 * @param {Object} o An Array of row objects which represents the dataset.
12347 * @return {Object} data A data block which is used by an Roo.data.Store object as
12348 * a cache of Roo.data.Records.
12350 readRecords : function(o){
12351 var sid = this.meta ? this.meta.id : null;
12352 var recordType = this.recordType, fields = recordType.prototype.fields;
12355 for(var i = 0; i < root.length; i++){
12358 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12359 for(var j = 0, jlen = fields.length; j < jlen; j++){
12360 var f = fields.items[j];
12361 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12362 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12364 values[f.name] = v;
12366 var record = new recordType(values, id);
12368 records[records.length] = record;
12372 totalRecords : records.length
12381 * @class Roo.bootstrap.ComboBox
12382 * @extends Roo.bootstrap.TriggerField
12383 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12384 * @cfg {Boolean} append (true|false) default false
12385 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12386 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12387 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12388 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12389 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12390 * @cfg {Boolean} animate default true
12391 * @cfg {Boolean} emptyResultText only for touch device
12392 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12393 * @cfg {String} emptyTitle default ''
12395 * Create a new ComboBox.
12396 * @param {Object} config Configuration options
12398 Roo.bootstrap.ComboBox = function(config){
12399 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12403 * Fires when the dropdown list is expanded
12404 * @param {Roo.bootstrap.ComboBox} combo This combo box
12409 * Fires when the dropdown list is collapsed
12410 * @param {Roo.bootstrap.ComboBox} combo This combo box
12414 * @event beforeselect
12415 * Fires before a list item is selected. Return false to cancel the selection.
12416 * @param {Roo.bootstrap.ComboBox} combo This combo box
12417 * @param {Roo.data.Record} record The data record returned from the underlying store
12418 * @param {Number} index The index of the selected item in the dropdown list
12420 'beforeselect' : true,
12423 * Fires when a list item is selected
12424 * @param {Roo.bootstrap.ComboBox} combo This combo box
12425 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12426 * @param {Number} index The index of the selected item in the dropdown list
12430 * @event beforequery
12431 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12432 * The event object passed has these properties:
12433 * @param {Roo.bootstrap.ComboBox} combo This combo box
12434 * @param {String} query The query
12435 * @param {Boolean} forceAll true to force "all" query
12436 * @param {Boolean} cancel true to cancel the query
12437 * @param {Object} e The query event object
12439 'beforequery': true,
12442 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12443 * @param {Roo.bootstrap.ComboBox} combo This combo box
12448 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12449 * @param {Roo.bootstrap.ComboBox} combo This combo box
12450 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12455 * Fires when the remove value from the combobox array
12456 * @param {Roo.bootstrap.ComboBox} combo This combo box
12460 * @event afterremove
12461 * Fires when the remove value from the combobox array
12462 * @param {Roo.bootstrap.ComboBox} combo This combo box
12464 'afterremove' : true,
12466 * @event specialfilter
12467 * Fires when specialfilter
12468 * @param {Roo.bootstrap.ComboBox} combo This combo box
12470 'specialfilter' : true,
12473 * Fires when tick the element
12474 * @param {Roo.bootstrap.ComboBox} combo This combo box
12478 * @event touchviewdisplay
12479 * Fires when touch view require special display (default is using displayField)
12480 * @param {Roo.bootstrap.ComboBox} combo This combo box
12481 * @param {Object} cfg set html .
12483 'touchviewdisplay' : true
12488 this.tickItems = [];
12490 this.selectedIndex = -1;
12491 if(this.mode == 'local'){
12492 if(config.queryDelay === undefined){
12493 this.queryDelay = 10;
12495 if(config.minChars === undefined){
12501 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12504 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12505 * rendering into an Roo.Editor, defaults to false)
12508 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12509 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12512 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12515 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12516 * the dropdown list (defaults to undefined, with no header element)
12520 * @cfg {String/Roo.Template} tpl The template to use to render the output
12524 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12526 listWidth: undefined,
12528 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12529 * mode = 'remote' or 'text' if mode = 'local')
12531 displayField: undefined,
12534 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12535 * mode = 'remote' or 'value' if mode = 'local').
12536 * Note: use of a valueField requires the user make a selection
12537 * in order for a value to be mapped.
12539 valueField: undefined,
12541 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12546 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12547 * field's data value (defaults to the underlying DOM element's name)
12549 hiddenName: undefined,
12551 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12555 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12557 selectedClass: 'active',
12560 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12564 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12565 * anchor positions (defaults to 'tl-bl')
12567 listAlign: 'tl-bl?',
12569 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12573 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12574 * query specified by the allQuery config option (defaults to 'query')
12576 triggerAction: 'query',
12578 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12579 * (defaults to 4, does not apply if editable = false)
12583 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12584 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12588 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12589 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12593 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12594 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12598 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12599 * when editable = true (defaults to false)
12601 selectOnFocus:false,
12603 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12605 queryParam: 'query',
12607 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12608 * when mode = 'remote' (defaults to 'Loading...')
12610 loadingText: 'Loading...',
12612 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12616 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12620 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12621 * traditional select (defaults to true)
12625 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12629 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12633 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12634 * listWidth has a higher value)
12638 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12639 * allow the user to set arbitrary text into the field (defaults to false)
12641 forceSelection:false,
12643 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12644 * if typeAhead = true (defaults to 250)
12646 typeAheadDelay : 250,
12648 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12649 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12651 valueNotFoundText : undefined,
12653 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12655 blockFocus : false,
12658 * @cfg {Boolean} disableClear Disable showing of clear button.
12660 disableClear : false,
12662 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12664 alwaysQuery : false,
12667 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12672 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12674 invalidClass : "has-warning",
12677 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12679 validClass : "has-success",
12682 * @cfg {Boolean} specialFilter (true|false) special filter default false
12684 specialFilter : false,
12687 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12689 mobileTouchView : true,
12692 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12694 useNativeIOS : false,
12696 ios_options : false,
12708 btnPosition : 'right',
12709 triggerList : true,
12710 showToggleBtn : true,
12712 emptyResultText: 'Empty',
12713 triggerText : 'Select',
12716 // element that contains real text value.. (when hidden is used..)
12718 getAutoCreate : function()
12723 * Render classic select for iso
12726 if(Roo.isIOS && this.useNativeIOS){
12727 cfg = this.getAutoCreateNativeIOS();
12735 if(Roo.isTouch && this.mobileTouchView){
12736 cfg = this.getAutoCreateTouchView();
12743 if(!this.tickable){
12744 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12749 * ComboBox with tickable selections
12752 var align = this.labelAlign || this.parentLabelAlign();
12755 cls : 'form-group roo-combobox-tickable' //input-group
12758 var btn_text_select = '';
12759 var btn_text_done = '';
12760 var btn_text_cancel = '';
12762 if (this.btn_text_show) {
12763 btn_text_select = 'Select';
12764 btn_text_done = 'Done';
12765 btn_text_cancel = 'Cancel';
12770 cls : 'tickable-buttons',
12775 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12776 //html : this.triggerText
12777 html: btn_text_select
12783 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12785 html: btn_text_done
12791 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12793 html: btn_text_cancel
12799 buttons.cn.unshift({
12801 cls: 'roo-select2-search-field-input'
12807 Roo.each(buttons.cn, function(c){
12809 c.cls += ' btn-' + _this.size;
12812 if (_this.disabled) {
12823 cls: 'form-hidden-field'
12827 cls: 'roo-select2-choices',
12831 cls: 'roo-select2-search-field',
12842 cls: 'roo-select2-container input-group roo-select2-container-multi',
12847 // cls: 'typeahead typeahead-long dropdown-menu',
12848 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12853 if(this.hasFeedback && !this.allowBlank){
12857 cls: 'glyphicon form-control-feedback'
12860 combobox.cn.push(feedback);
12864 if (align ==='left' && this.fieldLabel.length) {
12866 cfg.cls += ' roo-form-group-label-left';
12871 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12872 tooltip : 'This field is required'
12877 cls : 'control-label',
12878 html : this.fieldLabel
12890 var labelCfg = cfg.cn[1];
12891 var contentCfg = cfg.cn[2];
12894 if(this.indicatorpos == 'right'){
12900 cls : 'control-label',
12904 html : this.fieldLabel
12908 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12909 tooltip : 'This field is required'
12924 labelCfg = cfg.cn[0];
12925 contentCfg = cfg.cn[1];
12929 if(this.labelWidth > 12){
12930 labelCfg.style = "width: " + this.labelWidth + 'px';
12933 if(this.labelWidth < 13 && this.labelmd == 0){
12934 this.labelmd = this.labelWidth;
12937 if(this.labellg > 0){
12938 labelCfg.cls += ' col-lg-' + this.labellg;
12939 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12942 if(this.labelmd > 0){
12943 labelCfg.cls += ' col-md-' + this.labelmd;
12944 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12947 if(this.labelsm > 0){
12948 labelCfg.cls += ' col-sm-' + this.labelsm;
12949 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12952 if(this.labelxs > 0){
12953 labelCfg.cls += ' col-xs-' + this.labelxs;
12954 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12958 } else if ( this.fieldLabel.length) {
12959 // Roo.log(" label");
12963 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12964 tooltip : 'This field is required'
12968 //cls : 'input-group-addon',
12969 html : this.fieldLabel
12974 if(this.indicatorpos == 'right'){
12978 //cls : 'input-group-addon',
12979 html : this.fieldLabel
12983 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12984 tooltip : 'This field is required'
12993 // Roo.log(" no label && no align");
13000 ['xs','sm','md','lg'].map(function(size){
13001 if (settings[size]) {
13002 cfg.cls += ' col-' + size + '-' + settings[size];
13010 _initEventsCalled : false,
13013 initEvents: function()
13015 if (this._initEventsCalled) { // as we call render... prevent looping...
13018 this._initEventsCalled = true;
13021 throw "can not find store for combo";
13024 this.indicator = this.indicatorEl();
13026 this.store = Roo.factory(this.store, Roo.data);
13027 this.store.parent = this;
13029 // if we are building from html. then this element is so complex, that we can not really
13030 // use the rendered HTML.
13031 // so we have to trash and replace the previous code.
13032 if (Roo.XComponent.build_from_html) {
13033 // remove this element....
13034 var e = this.el.dom, k=0;
13035 while (e ) { e = e.previousSibling; ++k;}
13040 this.rendered = false;
13042 this.render(this.parent().getChildContainer(true), k);
13045 if(Roo.isIOS && this.useNativeIOS){
13046 this.initIOSView();
13054 if(Roo.isTouch && this.mobileTouchView){
13055 this.initTouchView();
13060 this.initTickableEvents();
13064 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13066 if(this.hiddenName){
13068 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13070 this.hiddenField.dom.value =
13071 this.hiddenValue !== undefined ? this.hiddenValue :
13072 this.value !== undefined ? this.value : '';
13074 // prevent input submission
13075 this.el.dom.removeAttribute('name');
13076 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13081 // this.el.dom.setAttribute('autocomplete', 'off');
13084 var cls = 'x-combo-list';
13086 //this.list = new Roo.Layer({
13087 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13093 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13094 _this.list.setWidth(lw);
13097 this.list.on('mouseover', this.onViewOver, this);
13098 this.list.on('mousemove', this.onViewMove, this);
13099 this.list.on('scroll', this.onViewScroll, this);
13102 this.list.swallowEvent('mousewheel');
13103 this.assetHeight = 0;
13106 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13107 this.assetHeight += this.header.getHeight();
13110 this.innerList = this.list.createChild({cls:cls+'-inner'});
13111 this.innerList.on('mouseover', this.onViewOver, this);
13112 this.innerList.on('mousemove', this.onViewMove, this);
13113 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13115 if(this.allowBlank && !this.pageSize && !this.disableClear){
13116 this.footer = this.list.createChild({cls:cls+'-ft'});
13117 this.pageTb = new Roo.Toolbar(this.footer);
13121 this.footer = this.list.createChild({cls:cls+'-ft'});
13122 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13123 {pageSize: this.pageSize});
13127 if (this.pageTb && this.allowBlank && !this.disableClear) {
13129 this.pageTb.add(new Roo.Toolbar.Fill(), {
13130 cls: 'x-btn-icon x-btn-clear',
13132 handler: function()
13135 _this.clearValue();
13136 _this.onSelect(false, -1);
13141 this.assetHeight += this.footer.getHeight();
13146 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13149 this.view = new Roo.View(this.list, this.tpl, {
13150 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13152 //this.view.wrapEl.setDisplayed(false);
13153 this.view.on('click', this.onViewClick, this);
13156 this.store.on('beforeload', this.onBeforeLoad, this);
13157 this.store.on('load', this.onLoad, this);
13158 this.store.on('loadexception', this.onLoadException, this);
13160 if(this.resizable){
13161 this.resizer = new Roo.Resizable(this.list, {
13162 pinned:true, handles:'se'
13164 this.resizer.on('resize', function(r, w, h){
13165 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13166 this.listWidth = w;
13167 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13168 this.restrictHeight();
13170 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13173 if(!this.editable){
13174 this.editable = true;
13175 this.setEditable(false);
13180 if (typeof(this.events.add.listeners) != 'undefined') {
13182 this.addicon = this.wrap.createChild(
13183 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13185 this.addicon.on('click', function(e) {
13186 this.fireEvent('add', this);
13189 if (typeof(this.events.edit.listeners) != 'undefined') {
13191 this.editicon = this.wrap.createChild(
13192 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13193 if (this.addicon) {
13194 this.editicon.setStyle('margin-left', '40px');
13196 this.editicon.on('click', function(e) {
13198 // we fire even if inothing is selected..
13199 this.fireEvent('edit', this, this.lastData );
13205 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13206 "up" : function(e){
13207 this.inKeyMode = true;
13211 "down" : function(e){
13212 if(!this.isExpanded()){
13213 this.onTriggerClick();
13215 this.inKeyMode = true;
13220 "enter" : function(e){
13221 // this.onViewClick();
13225 if(this.fireEvent("specialkey", this, e)){
13226 this.onViewClick(false);
13232 "esc" : function(e){
13236 "tab" : function(e){
13239 if(this.fireEvent("specialkey", this, e)){
13240 this.onViewClick(false);
13248 doRelay : function(foo, bar, hname){
13249 if(hname == 'down' || this.scope.isExpanded()){
13250 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13259 this.queryDelay = Math.max(this.queryDelay || 10,
13260 this.mode == 'local' ? 10 : 250);
13263 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13265 if(this.typeAhead){
13266 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13268 if(this.editable !== false){
13269 this.inputEl().on("keyup", this.onKeyUp, this);
13271 if(this.forceSelection){
13272 this.inputEl().on('blur', this.doForce, this);
13276 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13277 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13281 initTickableEvents: function()
13285 if(this.hiddenName){
13287 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13289 this.hiddenField.dom.value =
13290 this.hiddenValue !== undefined ? this.hiddenValue :
13291 this.value !== undefined ? this.value : '';
13293 // prevent input submission
13294 this.el.dom.removeAttribute('name');
13295 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13300 // this.list = this.el.select('ul.dropdown-menu',true).first();
13302 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13303 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13304 if(this.triggerList){
13305 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13308 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13309 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13311 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13312 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13314 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13315 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13317 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13318 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13319 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13322 this.cancelBtn.hide();
13327 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13328 _this.list.setWidth(lw);
13331 this.list.on('mouseover', this.onViewOver, this);
13332 this.list.on('mousemove', this.onViewMove, this);
13334 this.list.on('scroll', this.onViewScroll, this);
13337 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>';
13340 this.view = new Roo.View(this.list, this.tpl, {
13341 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13344 //this.view.wrapEl.setDisplayed(false);
13345 this.view.on('click', this.onViewClick, this);
13349 this.store.on('beforeload', this.onBeforeLoad, this);
13350 this.store.on('load', this.onLoad, this);
13351 this.store.on('loadexception', this.onLoadException, this);
13354 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13355 "up" : function(e){
13356 this.inKeyMode = true;
13360 "down" : function(e){
13361 this.inKeyMode = true;
13365 "enter" : function(e){
13366 if(this.fireEvent("specialkey", this, e)){
13367 this.onViewClick(false);
13373 "esc" : function(e){
13374 this.onTickableFooterButtonClick(e, false, false);
13377 "tab" : function(e){
13378 this.fireEvent("specialkey", this, e);
13380 this.onTickableFooterButtonClick(e, false, false);
13387 doRelay : function(e, fn, key){
13388 if(this.scope.isExpanded()){
13389 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13398 this.queryDelay = Math.max(this.queryDelay || 10,
13399 this.mode == 'local' ? 10 : 250);
13402 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13404 if(this.typeAhead){
13405 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13408 if(this.editable !== false){
13409 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13412 this.indicator = this.indicatorEl();
13414 if(this.indicator){
13415 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13416 this.indicator.hide();
13421 onDestroy : function(){
13423 this.view.setStore(null);
13424 this.view.el.removeAllListeners();
13425 this.view.el.remove();
13426 this.view.purgeListeners();
13429 this.list.dom.innerHTML = '';
13433 this.store.un('beforeload', this.onBeforeLoad, this);
13434 this.store.un('load', this.onLoad, this);
13435 this.store.un('loadexception', this.onLoadException, this);
13437 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13441 fireKey : function(e){
13442 if(e.isNavKeyPress() && !this.list.isVisible()){
13443 this.fireEvent("specialkey", this, e);
13448 onResize: function(w, h){
13449 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13451 // if(typeof w != 'number'){
13452 // // we do not handle it!?!?
13455 // var tw = this.trigger.getWidth();
13456 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13457 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13459 // this.inputEl().setWidth( this.adjustWidth('input', x));
13461 // //this.trigger.setStyle('left', x+'px');
13463 // if(this.list && this.listWidth === undefined){
13464 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13465 // this.list.setWidth(lw);
13466 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13474 * Allow or prevent the user from directly editing the field text. If false is passed,
13475 * the user will only be able to select from the items defined in the dropdown list. This method
13476 * is the runtime equivalent of setting the 'editable' config option at config time.
13477 * @param {Boolean} value True to allow the user to directly edit the field text
13479 setEditable : function(value){
13480 if(value == this.editable){
13483 this.editable = value;
13485 this.inputEl().dom.setAttribute('readOnly', true);
13486 this.inputEl().on('mousedown', this.onTriggerClick, this);
13487 this.inputEl().addClass('x-combo-noedit');
13489 this.inputEl().dom.setAttribute('readOnly', false);
13490 this.inputEl().un('mousedown', this.onTriggerClick, this);
13491 this.inputEl().removeClass('x-combo-noedit');
13497 onBeforeLoad : function(combo,opts){
13498 if(!this.hasFocus){
13502 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13504 this.restrictHeight();
13505 this.selectedIndex = -1;
13509 onLoad : function(){
13511 this.hasQuery = false;
13513 if(!this.hasFocus){
13517 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13518 this.loading.hide();
13521 if(this.store.getCount() > 0){
13524 this.restrictHeight();
13525 if(this.lastQuery == this.allQuery){
13526 if(this.editable && !this.tickable){
13527 this.inputEl().dom.select();
13531 !this.selectByValue(this.value, true) &&
13534 !this.store.lastOptions ||
13535 typeof(this.store.lastOptions.add) == 'undefined' ||
13536 this.store.lastOptions.add != true
13539 this.select(0, true);
13542 if(this.autoFocus){
13545 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13546 this.taTask.delay(this.typeAheadDelay);
13550 this.onEmptyResults();
13556 onLoadException : function()
13558 this.hasQuery = false;
13560 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13561 this.loading.hide();
13564 if(this.tickable && this.editable){
13569 // only causes errors at present
13570 //Roo.log(this.store.reader.jsonData);
13571 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13573 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13579 onTypeAhead : function(){
13580 if(this.store.getCount() > 0){
13581 var r = this.store.getAt(0);
13582 var newValue = r.data[this.displayField];
13583 var len = newValue.length;
13584 var selStart = this.getRawValue().length;
13586 if(selStart != len){
13587 this.setRawValue(newValue);
13588 this.selectText(selStart, newValue.length);
13594 onSelect : function(record, index){
13596 if(this.fireEvent('beforeselect', this, record, index) !== false){
13598 this.setFromData(index > -1 ? record.data : false);
13601 this.fireEvent('select', this, record, index);
13606 * Returns the currently selected field value or empty string if no value is set.
13607 * @return {String} value The selected value
13609 getValue : function()
13611 if(Roo.isIOS && this.useNativeIOS){
13612 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13616 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13619 if(this.valueField){
13620 return typeof this.value != 'undefined' ? this.value : '';
13622 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13626 getRawValue : function()
13628 if(Roo.isIOS && this.useNativeIOS){
13629 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13632 var v = this.inputEl().getValue();
13638 * Clears any text/value currently set in the field
13640 clearValue : function(){
13642 if(this.hiddenField){
13643 this.hiddenField.dom.value = '';
13646 this.setRawValue('');
13647 this.lastSelectionText = '';
13648 this.lastData = false;
13650 var close = this.closeTriggerEl();
13661 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13662 * will be displayed in the field. If the value does not match the data value of an existing item,
13663 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13664 * Otherwise the field will be blank (although the value will still be set).
13665 * @param {String} value The value to match
13667 setValue : function(v)
13669 if(Roo.isIOS && this.useNativeIOS){
13670 this.setIOSValue(v);
13680 if(this.valueField){
13681 var r = this.findRecord(this.valueField, v);
13683 text = r.data[this.displayField];
13684 }else if(this.valueNotFoundText !== undefined){
13685 text = this.valueNotFoundText;
13688 this.lastSelectionText = text;
13689 if(this.hiddenField){
13690 this.hiddenField.dom.value = v;
13692 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13695 var close = this.closeTriggerEl();
13698 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13704 * @property {Object} the last set data for the element
13709 * Sets the value of the field based on a object which is related to the record format for the store.
13710 * @param {Object} value the value to set as. or false on reset?
13712 setFromData : function(o){
13719 var dv = ''; // display value
13720 var vv = ''; // value value..
13722 if (this.displayField) {
13723 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13725 // this is an error condition!!!
13726 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13729 if(this.valueField){
13730 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13733 var close = this.closeTriggerEl();
13736 if(dv.length || vv * 1 > 0){
13738 this.blockFocus=true;
13744 if(this.hiddenField){
13745 this.hiddenField.dom.value = vv;
13747 this.lastSelectionText = dv;
13748 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13752 // no hidden field.. - we store the value in 'value', but still display
13753 // display field!!!!
13754 this.lastSelectionText = dv;
13755 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13762 reset : function(){
13763 // overridden so that last data is reset..
13770 this.setValue(this.originalValue);
13771 //this.clearInvalid();
13772 this.lastData = false;
13774 this.view.clearSelections();
13780 findRecord : function(prop, value){
13782 if(this.store.getCount() > 0){
13783 this.store.each(function(r){
13784 if(r.data[prop] == value){
13794 getName: function()
13796 // returns hidden if it's set..
13797 if (!this.rendered) {return ''};
13798 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13802 onViewMove : function(e, t){
13803 this.inKeyMode = false;
13807 onViewOver : function(e, t){
13808 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13811 var item = this.view.findItemFromChild(t);
13814 var index = this.view.indexOf(item);
13815 this.select(index, false);
13820 onViewClick : function(view, doFocus, el, e)
13822 var index = this.view.getSelectedIndexes()[0];
13824 var r = this.store.getAt(index);
13828 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13835 Roo.each(this.tickItems, function(v,k){
13837 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13839 _this.tickItems.splice(k, 1);
13841 if(typeof(e) == 'undefined' && view == false){
13842 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13854 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13855 this.tickItems.push(r.data);
13858 if(typeof(e) == 'undefined' && view == false){
13859 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13866 this.onSelect(r, index);
13868 if(doFocus !== false && !this.blockFocus){
13869 this.inputEl().focus();
13874 restrictHeight : function(){
13875 //this.innerList.dom.style.height = '';
13876 //var inner = this.innerList.dom;
13877 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13878 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13879 //this.list.beginUpdate();
13880 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13881 this.list.alignTo(this.inputEl(), this.listAlign);
13882 this.list.alignTo(this.inputEl(), this.listAlign);
13883 //this.list.endUpdate();
13887 onEmptyResults : function(){
13889 if(this.tickable && this.editable){
13890 this.restrictHeight();
13898 * Returns true if the dropdown list is expanded, else false.
13900 isExpanded : function(){
13901 return this.list.isVisible();
13905 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13906 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13907 * @param {String} value The data value of the item to select
13908 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13909 * selected item if it is not currently in view (defaults to true)
13910 * @return {Boolean} True if the value matched an item in the list, else false
13912 selectByValue : function(v, scrollIntoView){
13913 if(v !== undefined && v !== null){
13914 var r = this.findRecord(this.valueField || this.displayField, v);
13916 this.select(this.store.indexOf(r), scrollIntoView);
13924 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13925 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13926 * @param {Number} index The zero-based index of the list item to select
13927 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13928 * selected item if it is not currently in view (defaults to true)
13930 select : function(index, scrollIntoView){
13931 this.selectedIndex = index;
13932 this.view.select(index);
13933 if(scrollIntoView !== false){
13934 var el = this.view.getNode(index);
13936 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13939 this.list.scrollChildIntoView(el, false);
13945 selectNext : function(){
13946 var ct = this.store.getCount();
13948 if(this.selectedIndex == -1){
13950 }else if(this.selectedIndex < ct-1){
13951 this.select(this.selectedIndex+1);
13957 selectPrev : function(){
13958 var ct = this.store.getCount();
13960 if(this.selectedIndex == -1){
13962 }else if(this.selectedIndex != 0){
13963 this.select(this.selectedIndex-1);
13969 onKeyUp : function(e){
13970 if(this.editable !== false && !e.isSpecialKey()){
13971 this.lastKey = e.getKey();
13972 this.dqTask.delay(this.queryDelay);
13977 validateBlur : function(){
13978 return !this.list || !this.list.isVisible();
13982 initQuery : function(){
13984 var v = this.getRawValue();
13986 if(this.tickable && this.editable){
13987 v = this.tickableInputEl().getValue();
13994 doForce : function(){
13995 if(this.inputEl().dom.value.length > 0){
13996 this.inputEl().dom.value =
13997 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14003 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14004 * query allowing the query action to be canceled if needed.
14005 * @param {String} query The SQL query to execute
14006 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14007 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14008 * saved in the current store (defaults to false)
14010 doQuery : function(q, forceAll){
14012 if(q === undefined || q === null){
14017 forceAll: forceAll,
14021 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14026 forceAll = qe.forceAll;
14027 if(forceAll === true || (q.length >= this.minChars)){
14029 this.hasQuery = true;
14031 if(this.lastQuery != q || this.alwaysQuery){
14032 this.lastQuery = q;
14033 if(this.mode == 'local'){
14034 this.selectedIndex = -1;
14036 this.store.clearFilter();
14039 if(this.specialFilter){
14040 this.fireEvent('specialfilter', this);
14045 this.store.filter(this.displayField, q);
14048 this.store.fireEvent("datachanged", this.store);
14055 this.store.baseParams[this.queryParam] = q;
14057 var options = {params : this.getParams(q)};
14060 options.add = true;
14061 options.params.start = this.page * this.pageSize;
14064 this.store.load(options);
14067 * this code will make the page width larger, at the beginning, the list not align correctly,
14068 * we should expand the list on onLoad
14069 * so command out it
14074 this.selectedIndex = -1;
14079 this.loadNext = false;
14083 getParams : function(q){
14085 //p[this.queryParam] = q;
14089 p.limit = this.pageSize;
14095 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14097 collapse : function(){
14098 if(!this.isExpanded()){
14104 this.hasFocus = false;
14108 this.cancelBtn.hide();
14109 this.trigger.show();
14112 this.tickableInputEl().dom.value = '';
14113 this.tickableInputEl().blur();
14118 Roo.get(document).un('mousedown', this.collapseIf, this);
14119 Roo.get(document).un('mousewheel', this.collapseIf, this);
14120 if (!this.editable) {
14121 Roo.get(document).un('keydown', this.listKeyPress, this);
14123 this.fireEvent('collapse', this);
14129 collapseIf : function(e){
14130 var in_combo = e.within(this.el);
14131 var in_list = e.within(this.list);
14132 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14134 if (in_combo || in_list || is_list) {
14135 //e.stopPropagation();
14140 this.onTickableFooterButtonClick(e, false, false);
14148 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14150 expand : function(){
14152 if(this.isExpanded() || !this.hasFocus){
14156 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14157 this.list.setWidth(lw);
14163 this.restrictHeight();
14167 this.tickItems = Roo.apply([], this.item);
14170 this.cancelBtn.show();
14171 this.trigger.hide();
14174 this.tickableInputEl().focus();
14179 Roo.get(document).on('mousedown', this.collapseIf, this);
14180 Roo.get(document).on('mousewheel', this.collapseIf, this);
14181 if (!this.editable) {
14182 Roo.get(document).on('keydown', this.listKeyPress, this);
14185 this.fireEvent('expand', this);
14189 // Implements the default empty TriggerField.onTriggerClick function
14190 onTriggerClick : function(e)
14192 Roo.log('trigger click');
14194 if(this.disabled || !this.triggerList){
14199 this.loadNext = false;
14201 if(this.isExpanded()){
14203 if (!this.blockFocus) {
14204 this.inputEl().focus();
14208 this.hasFocus = true;
14209 if(this.triggerAction == 'all') {
14210 this.doQuery(this.allQuery, true);
14212 this.doQuery(this.getRawValue());
14214 if (!this.blockFocus) {
14215 this.inputEl().focus();
14220 onTickableTriggerClick : function(e)
14227 this.loadNext = false;
14228 this.hasFocus = true;
14230 if(this.triggerAction == 'all') {
14231 this.doQuery(this.allQuery, true);
14233 this.doQuery(this.getRawValue());
14237 onSearchFieldClick : function(e)
14239 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14240 this.onTickableFooterButtonClick(e, false, false);
14244 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14249 this.loadNext = false;
14250 this.hasFocus = true;
14252 if(this.triggerAction == 'all') {
14253 this.doQuery(this.allQuery, true);
14255 this.doQuery(this.getRawValue());
14259 listKeyPress : function(e)
14261 //Roo.log('listkeypress');
14262 // scroll to first matching element based on key pres..
14263 if (e.isSpecialKey()) {
14266 var k = String.fromCharCode(e.getKey()).toUpperCase();
14269 var csel = this.view.getSelectedNodes();
14270 var cselitem = false;
14272 var ix = this.view.indexOf(csel[0]);
14273 cselitem = this.store.getAt(ix);
14274 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14280 this.store.each(function(v) {
14282 // start at existing selection.
14283 if (cselitem.id == v.id) {
14289 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14290 match = this.store.indexOf(v);
14296 if (match === false) {
14297 return true; // no more action?
14300 this.view.select(match);
14301 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14302 sn.scrollIntoView(sn.dom.parentNode, false);
14305 onViewScroll : function(e, t){
14307 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){
14311 this.hasQuery = true;
14313 this.loading = this.list.select('.loading', true).first();
14315 if(this.loading === null){
14316 this.list.createChild({
14318 cls: 'loading roo-select2-more-results roo-select2-active',
14319 html: 'Loading more results...'
14322 this.loading = this.list.select('.loading', true).first();
14324 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14326 this.loading.hide();
14329 this.loading.show();
14334 this.loadNext = true;
14336 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14341 addItem : function(o)
14343 var dv = ''; // display value
14345 if (this.displayField) {
14346 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14348 // this is an error condition!!!
14349 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14356 var choice = this.choices.createChild({
14358 cls: 'roo-select2-search-choice',
14367 cls: 'roo-select2-search-choice-close fa fa-times',
14372 }, this.searchField);
14374 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14376 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14384 this.inputEl().dom.value = '';
14389 onRemoveItem : function(e, _self, o)
14391 e.preventDefault();
14393 this.lastItem = Roo.apply([], this.item);
14395 var index = this.item.indexOf(o.data) * 1;
14398 Roo.log('not this item?!');
14402 this.item.splice(index, 1);
14407 this.fireEvent('remove', this, e);
14413 syncValue : function()
14415 if(!this.item.length){
14422 Roo.each(this.item, function(i){
14423 if(_this.valueField){
14424 value.push(i[_this.valueField]);
14431 this.value = value.join(',');
14433 if(this.hiddenField){
14434 this.hiddenField.dom.value = this.value;
14437 this.store.fireEvent("datachanged", this.store);
14442 clearItem : function()
14444 if(!this.multiple){
14450 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14458 if(this.tickable && !Roo.isTouch){
14459 this.view.refresh();
14463 inputEl: function ()
14465 if(Roo.isIOS && this.useNativeIOS){
14466 return this.el.select('select.roo-ios-select', true).first();
14469 if(Roo.isTouch && this.mobileTouchView){
14470 return this.el.select('input.form-control',true).first();
14474 return this.searchField;
14477 return this.el.select('input.form-control',true).first();
14480 onTickableFooterButtonClick : function(e, btn, el)
14482 e.preventDefault();
14484 this.lastItem = Roo.apply([], this.item);
14486 if(btn && btn.name == 'cancel'){
14487 this.tickItems = Roo.apply([], this.item);
14496 Roo.each(this.tickItems, function(o){
14504 validate : function()
14506 var v = this.getRawValue();
14509 v = this.getValue();
14512 if(this.disabled || this.allowBlank || v.length){
14517 this.markInvalid();
14521 tickableInputEl : function()
14523 if(!this.tickable || !this.editable){
14524 return this.inputEl();
14527 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14531 getAutoCreateTouchView : function()
14536 cls: 'form-group' //input-group
14542 type : this.inputType,
14543 cls : 'form-control x-combo-noedit',
14544 autocomplete: 'new-password',
14545 placeholder : this.placeholder || '',
14550 input.name = this.name;
14554 input.cls += ' input-' + this.size;
14557 if (this.disabled) {
14558 input.disabled = true;
14569 inputblock.cls += ' input-group';
14571 inputblock.cn.unshift({
14573 cls : 'input-group-addon',
14578 if(this.removable && !this.multiple){
14579 inputblock.cls += ' roo-removable';
14581 inputblock.cn.push({
14584 cls : 'roo-combo-removable-btn close'
14588 if(this.hasFeedback && !this.allowBlank){
14590 inputblock.cls += ' has-feedback';
14592 inputblock.cn.push({
14594 cls: 'glyphicon form-control-feedback'
14601 inputblock.cls += (this.before) ? '' : ' input-group';
14603 inputblock.cn.push({
14605 cls : 'input-group-addon',
14616 cls: 'form-hidden-field'
14630 cls: 'form-hidden-field'
14634 cls: 'roo-select2-choices',
14638 cls: 'roo-select2-search-field',
14651 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14657 if(!this.multiple && this.showToggleBtn){
14664 if (this.caret != false) {
14667 cls: 'fa fa-' + this.caret
14674 cls : 'input-group-addon btn dropdown-toggle',
14679 cls: 'combobox-clear',
14693 combobox.cls += ' roo-select2-container-multi';
14696 var align = this.labelAlign || this.parentLabelAlign();
14698 if (align ==='left' && this.fieldLabel.length) {
14703 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14704 tooltip : 'This field is required'
14708 cls : 'control-label',
14709 html : this.fieldLabel
14720 var labelCfg = cfg.cn[1];
14721 var contentCfg = cfg.cn[2];
14724 if(this.indicatorpos == 'right'){
14729 cls : 'control-label',
14733 html : this.fieldLabel
14737 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14738 tooltip : 'This field is required'
14751 labelCfg = cfg.cn[0];
14752 contentCfg = cfg.cn[1];
14757 if(this.labelWidth > 12){
14758 labelCfg.style = "width: " + this.labelWidth + 'px';
14761 if(this.labelWidth < 13 && this.labelmd == 0){
14762 this.labelmd = this.labelWidth;
14765 if(this.labellg > 0){
14766 labelCfg.cls += ' col-lg-' + this.labellg;
14767 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14770 if(this.labelmd > 0){
14771 labelCfg.cls += ' col-md-' + this.labelmd;
14772 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14775 if(this.labelsm > 0){
14776 labelCfg.cls += ' col-sm-' + this.labelsm;
14777 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14780 if(this.labelxs > 0){
14781 labelCfg.cls += ' col-xs-' + this.labelxs;
14782 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14786 } else if ( this.fieldLabel.length) {
14790 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14791 tooltip : 'This field is required'
14795 cls : 'control-label',
14796 html : this.fieldLabel
14807 if(this.indicatorpos == 'right'){
14811 cls : 'control-label',
14812 html : this.fieldLabel,
14816 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14817 tooltip : 'This field is required'
14834 var settings = this;
14836 ['xs','sm','md','lg'].map(function(size){
14837 if (settings[size]) {
14838 cfg.cls += ' col-' + size + '-' + settings[size];
14845 initTouchView : function()
14847 this.renderTouchView();
14849 this.touchViewEl.on('scroll', function(){
14850 this.el.dom.scrollTop = 0;
14853 this.originalValue = this.getValue();
14855 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14857 this.inputEl().on("click", this.showTouchView, this);
14858 if (this.triggerEl) {
14859 this.triggerEl.on("click", this.showTouchView, this);
14863 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14864 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14866 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14868 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14869 this.store.on('load', this.onTouchViewLoad, this);
14870 this.store.on('loadexception', this.onTouchViewLoadException, this);
14872 if(this.hiddenName){
14874 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14876 this.hiddenField.dom.value =
14877 this.hiddenValue !== undefined ? this.hiddenValue :
14878 this.value !== undefined ? this.value : '';
14880 this.el.dom.removeAttribute('name');
14881 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14885 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14886 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14889 if(this.removable && !this.multiple){
14890 var close = this.closeTriggerEl();
14892 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14893 close.on('click', this.removeBtnClick, this, close);
14897 * fix the bug in Safari iOS8
14899 this.inputEl().on("focus", function(e){
14900 document.activeElement.blur();
14908 renderTouchView : function()
14910 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14911 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14913 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14914 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14916 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14917 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14918 this.touchViewBodyEl.setStyle('overflow', 'auto');
14920 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14921 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14923 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14924 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14928 showTouchView : function()
14934 this.touchViewHeaderEl.hide();
14936 if(this.modalTitle.length){
14937 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14938 this.touchViewHeaderEl.show();
14941 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14942 this.touchViewEl.show();
14944 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14946 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14947 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14949 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14951 if(this.modalTitle.length){
14952 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14955 this.touchViewBodyEl.setHeight(bodyHeight);
14959 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14961 this.touchViewEl.addClass('in');
14964 this.doTouchViewQuery();
14968 hideTouchView : function()
14970 this.touchViewEl.removeClass('in');
14974 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14976 this.touchViewEl.setStyle('display', 'none');
14981 setTouchViewValue : function()
14988 Roo.each(this.tickItems, function(o){
14993 this.hideTouchView();
14996 doTouchViewQuery : function()
15005 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15009 if(!this.alwaysQuery || this.mode == 'local'){
15010 this.onTouchViewLoad();
15017 onTouchViewBeforeLoad : function(combo,opts)
15023 onTouchViewLoad : function()
15025 if(this.store.getCount() < 1){
15026 this.onTouchViewEmptyResults();
15030 this.clearTouchView();
15032 var rawValue = this.getRawValue();
15034 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15036 this.tickItems = [];
15038 this.store.data.each(function(d, rowIndex){
15039 var row = this.touchViewListGroup.createChild(template);
15041 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15042 row.addClass(d.data.cls);
15045 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15048 html : d.data[this.displayField]
15051 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15052 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15055 row.removeClass('selected');
15056 if(!this.multiple && this.valueField &&
15057 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15060 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15061 row.addClass('selected');
15064 if(this.multiple && this.valueField &&
15065 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15069 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15070 this.tickItems.push(d.data);
15073 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15077 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15079 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15081 if(this.modalTitle.length){
15082 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15085 var listHeight = this.touchViewListGroup.getHeight();
15089 if(firstChecked && listHeight > bodyHeight){
15090 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15095 onTouchViewLoadException : function()
15097 this.hideTouchView();
15100 onTouchViewEmptyResults : function()
15102 this.clearTouchView();
15104 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15106 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15110 clearTouchView : function()
15112 this.touchViewListGroup.dom.innerHTML = '';
15115 onTouchViewClick : function(e, el, o)
15117 e.preventDefault();
15120 var rowIndex = o.rowIndex;
15122 var r = this.store.getAt(rowIndex);
15124 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15126 if(!this.multiple){
15127 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15128 c.dom.removeAttribute('checked');
15131 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15133 this.setFromData(r.data);
15135 var close = this.closeTriggerEl();
15141 this.hideTouchView();
15143 this.fireEvent('select', this, r, rowIndex);
15148 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15149 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15150 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15154 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15155 this.addItem(r.data);
15156 this.tickItems.push(r.data);
15160 getAutoCreateNativeIOS : function()
15163 cls: 'form-group' //input-group,
15168 cls : 'roo-ios-select'
15172 combobox.name = this.name;
15175 if (this.disabled) {
15176 combobox.disabled = true;
15179 var settings = this;
15181 ['xs','sm','md','lg'].map(function(size){
15182 if (settings[size]) {
15183 cfg.cls += ' col-' + size + '-' + settings[size];
15193 initIOSView : function()
15195 this.store.on('load', this.onIOSViewLoad, this);
15200 onIOSViewLoad : function()
15202 if(this.store.getCount() < 1){
15206 this.clearIOSView();
15208 if(this.allowBlank) {
15210 var default_text = '-- SELECT --';
15212 var opt = this.inputEl().createChild({
15215 html : default_text
15219 o[this.valueField] = 0;
15220 o[this.displayField] = default_text;
15222 this.ios_options.push({
15229 this.store.data.each(function(d, rowIndex){
15233 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15234 html = d.data[this.displayField];
15239 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15240 value = d.data[this.valueField];
15249 if(this.value == d.data[this.valueField]){
15250 option['selected'] = true;
15253 var opt = this.inputEl().createChild(option);
15255 this.ios_options.push({
15262 this.inputEl().on('change', function(){
15263 this.fireEvent('select', this);
15268 clearIOSView: function()
15270 this.inputEl().dom.innerHTML = '';
15272 this.ios_options = [];
15275 setIOSValue: function(v)
15279 if(!this.ios_options){
15283 Roo.each(this.ios_options, function(opts){
15285 opts.el.dom.removeAttribute('selected');
15287 if(opts.data[this.valueField] != v){
15291 opts.el.dom.setAttribute('selected', true);
15297 * @cfg {Boolean} grow
15301 * @cfg {Number} growMin
15305 * @cfg {Number} growMax
15314 Roo.apply(Roo.bootstrap.ComboBox, {
15318 cls: 'modal-header',
15340 cls: 'list-group-item',
15344 cls: 'roo-combobox-list-group-item-value'
15348 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15362 listItemCheckbox : {
15364 cls: 'list-group-item',
15368 cls: 'roo-combobox-list-group-item-value'
15372 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15388 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15393 cls: 'modal-footer',
15401 cls: 'col-xs-6 text-left',
15404 cls: 'btn btn-danger roo-touch-view-cancel',
15410 cls: 'col-xs-6 text-right',
15413 cls: 'btn btn-success roo-touch-view-ok',
15424 Roo.apply(Roo.bootstrap.ComboBox, {
15426 touchViewTemplate : {
15428 cls: 'modal fade roo-combobox-touch-view',
15432 cls: 'modal-dialog',
15433 style : 'position:fixed', // we have to fix position....
15437 cls: 'modal-content',
15439 Roo.bootstrap.ComboBox.header,
15440 Roo.bootstrap.ComboBox.body,
15441 Roo.bootstrap.ComboBox.footer
15450 * Ext JS Library 1.1.1
15451 * Copyright(c) 2006-2007, Ext JS, LLC.
15453 * Originally Released Under LGPL - original licence link has changed is not relivant.
15456 * <script type="text/javascript">
15461 * @extends Roo.util.Observable
15462 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15463 * This class also supports single and multi selection modes. <br>
15464 * Create a data model bound view:
15466 var store = new Roo.data.Store(...);
15468 var view = new Roo.View({
15470 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15472 singleSelect: true,
15473 selectedClass: "ydataview-selected",
15477 // listen for node click?
15478 view.on("click", function(vw, index, node, e){
15479 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15483 dataModel.load("foobar.xml");
15485 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15487 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15488 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15490 * Note: old style constructor is still suported (container, template, config)
15493 * Create a new View
15494 * @param {Object} config The config object
15497 Roo.View = function(config, depreciated_tpl, depreciated_config){
15499 this.parent = false;
15501 if (typeof(depreciated_tpl) == 'undefined') {
15502 // new way.. - universal constructor.
15503 Roo.apply(this, config);
15504 this.el = Roo.get(this.el);
15507 this.el = Roo.get(config);
15508 this.tpl = depreciated_tpl;
15509 Roo.apply(this, depreciated_config);
15511 this.wrapEl = this.el.wrap().wrap();
15512 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15515 if(typeof(this.tpl) == "string"){
15516 this.tpl = new Roo.Template(this.tpl);
15518 // support xtype ctors..
15519 this.tpl = new Roo.factory(this.tpl, Roo);
15523 this.tpl.compile();
15528 * @event beforeclick
15529 * Fires before a click is processed. Returns false to cancel the default action.
15530 * @param {Roo.View} this
15531 * @param {Number} index The index of the target node
15532 * @param {HTMLElement} node The target node
15533 * @param {Roo.EventObject} e The raw event object
15535 "beforeclick" : true,
15538 * Fires when a template node is clicked.
15539 * @param {Roo.View} this
15540 * @param {Number} index The index of the target node
15541 * @param {HTMLElement} node The target node
15542 * @param {Roo.EventObject} e The raw event object
15547 * Fires when a template node is double clicked.
15548 * @param {Roo.View} this
15549 * @param {Number} index The index of the target node
15550 * @param {HTMLElement} node The target node
15551 * @param {Roo.EventObject} e The raw event object
15555 * @event contextmenu
15556 * Fires when a template node is right clicked.
15557 * @param {Roo.View} this
15558 * @param {Number} index The index of the target node
15559 * @param {HTMLElement} node The target node
15560 * @param {Roo.EventObject} e The raw event object
15562 "contextmenu" : true,
15564 * @event selectionchange
15565 * Fires when the selected nodes change.
15566 * @param {Roo.View} this
15567 * @param {Array} selections Array of the selected nodes
15569 "selectionchange" : true,
15572 * @event beforeselect
15573 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15574 * @param {Roo.View} this
15575 * @param {HTMLElement} node The node to be selected
15576 * @param {Array} selections Array of currently selected nodes
15578 "beforeselect" : true,
15580 * @event preparedata
15581 * Fires on every row to render, to allow you to change the data.
15582 * @param {Roo.View} this
15583 * @param {Object} data to be rendered (change this)
15585 "preparedata" : true
15593 "click": this.onClick,
15594 "dblclick": this.onDblClick,
15595 "contextmenu": this.onContextMenu,
15599 this.selections = [];
15601 this.cmp = new Roo.CompositeElementLite([]);
15603 this.store = Roo.factory(this.store, Roo.data);
15604 this.setStore(this.store, true);
15607 if ( this.footer && this.footer.xtype) {
15609 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15611 this.footer.dataSource = this.store;
15612 this.footer.container = fctr;
15613 this.footer = Roo.factory(this.footer, Roo);
15614 fctr.insertFirst(this.el);
15616 // this is a bit insane - as the paging toolbar seems to detach the el..
15617 // dom.parentNode.parentNode.parentNode
15618 // they get detached?
15622 Roo.View.superclass.constructor.call(this);
15627 Roo.extend(Roo.View, Roo.util.Observable, {
15630 * @cfg {Roo.data.Store} store Data store to load data from.
15635 * @cfg {String|Roo.Element} el The container element.
15640 * @cfg {String|Roo.Template} tpl The template used by this View
15644 * @cfg {String} dataName the named area of the template to use as the data area
15645 * Works with domtemplates roo-name="name"
15649 * @cfg {String} selectedClass The css class to add to selected nodes
15651 selectedClass : "x-view-selected",
15653 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15658 * @cfg {String} text to display on mask (default Loading)
15662 * @cfg {Boolean} multiSelect Allow multiple selection
15664 multiSelect : false,
15666 * @cfg {Boolean} singleSelect Allow single selection
15668 singleSelect: false,
15671 * @cfg {Boolean} toggleSelect - selecting
15673 toggleSelect : false,
15676 * @cfg {Boolean} tickable - selecting
15681 * Returns the element this view is bound to.
15682 * @return {Roo.Element}
15684 getEl : function(){
15685 return this.wrapEl;
15691 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15693 refresh : function(){
15694 //Roo.log('refresh');
15697 // if we are using something like 'domtemplate', then
15698 // the what gets used is:
15699 // t.applySubtemplate(NAME, data, wrapping data..)
15700 // the outer template then get' applied with
15701 // the store 'extra data'
15702 // and the body get's added to the
15703 // roo-name="data" node?
15704 // <span class='roo-tpl-{name}'></span> ?????
15708 this.clearSelections();
15709 this.el.update("");
15711 var records = this.store.getRange();
15712 if(records.length < 1) {
15714 // is this valid?? = should it render a template??
15716 this.el.update(this.emptyText);
15720 if (this.dataName) {
15721 this.el.update(t.apply(this.store.meta)); //????
15722 el = this.el.child('.roo-tpl-' + this.dataName);
15725 for(var i = 0, len = records.length; i < len; i++){
15726 var data = this.prepareData(records[i].data, i, records[i]);
15727 this.fireEvent("preparedata", this, data, i, records[i]);
15729 var d = Roo.apply({}, data);
15732 Roo.apply(d, {'roo-id' : Roo.id()});
15736 Roo.each(this.parent.item, function(item){
15737 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15740 Roo.apply(d, {'roo-data-checked' : 'checked'});
15744 html[html.length] = Roo.util.Format.trim(
15746 t.applySubtemplate(this.dataName, d, this.store.meta) :
15753 el.update(html.join(""));
15754 this.nodes = el.dom.childNodes;
15755 this.updateIndexes(0);
15760 * Function to override to reformat the data that is sent to
15761 * the template for each node.
15762 * DEPRICATED - use the preparedata event handler.
15763 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15764 * a JSON object for an UpdateManager bound view).
15766 prepareData : function(data, index, record)
15768 this.fireEvent("preparedata", this, data, index, record);
15772 onUpdate : function(ds, record){
15773 // Roo.log('on update');
15774 this.clearSelections();
15775 var index = this.store.indexOf(record);
15776 var n = this.nodes[index];
15777 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15778 n.parentNode.removeChild(n);
15779 this.updateIndexes(index, index);
15785 onAdd : function(ds, records, index)
15787 //Roo.log(['on Add', ds, records, index] );
15788 this.clearSelections();
15789 if(this.nodes.length == 0){
15793 var n = this.nodes[index];
15794 for(var i = 0, len = records.length; i < len; i++){
15795 var d = this.prepareData(records[i].data, i, records[i]);
15797 this.tpl.insertBefore(n, d);
15800 this.tpl.append(this.el, d);
15803 this.updateIndexes(index);
15806 onRemove : function(ds, record, index){
15807 // Roo.log('onRemove');
15808 this.clearSelections();
15809 var el = this.dataName ?
15810 this.el.child('.roo-tpl-' + this.dataName) :
15813 el.dom.removeChild(this.nodes[index]);
15814 this.updateIndexes(index);
15818 * Refresh an individual node.
15819 * @param {Number} index
15821 refreshNode : function(index){
15822 this.onUpdate(this.store, this.store.getAt(index));
15825 updateIndexes : function(startIndex, endIndex){
15826 var ns = this.nodes;
15827 startIndex = startIndex || 0;
15828 endIndex = endIndex || ns.length - 1;
15829 for(var i = startIndex; i <= endIndex; i++){
15830 ns[i].nodeIndex = i;
15835 * Changes the data store this view uses and refresh the view.
15836 * @param {Store} store
15838 setStore : function(store, initial){
15839 if(!initial && this.store){
15840 this.store.un("datachanged", this.refresh);
15841 this.store.un("add", this.onAdd);
15842 this.store.un("remove", this.onRemove);
15843 this.store.un("update", this.onUpdate);
15844 this.store.un("clear", this.refresh);
15845 this.store.un("beforeload", this.onBeforeLoad);
15846 this.store.un("load", this.onLoad);
15847 this.store.un("loadexception", this.onLoad);
15851 store.on("datachanged", this.refresh, this);
15852 store.on("add", this.onAdd, this);
15853 store.on("remove", this.onRemove, this);
15854 store.on("update", this.onUpdate, this);
15855 store.on("clear", this.refresh, this);
15856 store.on("beforeload", this.onBeforeLoad, this);
15857 store.on("load", this.onLoad, this);
15858 store.on("loadexception", this.onLoad, this);
15866 * onbeforeLoad - masks the loading area.
15869 onBeforeLoad : function(store,opts)
15871 //Roo.log('onBeforeLoad');
15873 this.el.update("");
15875 this.el.mask(this.mask ? this.mask : "Loading" );
15877 onLoad : function ()
15884 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15885 * @param {HTMLElement} node
15886 * @return {HTMLElement} The template node
15888 findItemFromChild : function(node){
15889 var el = this.dataName ?
15890 this.el.child('.roo-tpl-' + this.dataName,true) :
15893 if(!node || node.parentNode == el){
15896 var p = node.parentNode;
15897 while(p && p != el){
15898 if(p.parentNode == el){
15907 onClick : function(e){
15908 var item = this.findItemFromChild(e.getTarget());
15910 var index = this.indexOf(item);
15911 if(this.onItemClick(item, index, e) !== false){
15912 this.fireEvent("click", this, index, item, e);
15915 this.clearSelections();
15920 onContextMenu : function(e){
15921 var item = this.findItemFromChild(e.getTarget());
15923 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15928 onDblClick : function(e){
15929 var item = this.findItemFromChild(e.getTarget());
15931 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15935 onItemClick : function(item, index, e)
15937 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15940 if (this.toggleSelect) {
15941 var m = this.isSelected(item) ? 'unselect' : 'select';
15944 _t[m](item, true, false);
15947 if(this.multiSelect || this.singleSelect){
15948 if(this.multiSelect && e.shiftKey && this.lastSelection){
15949 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15951 this.select(item, this.multiSelect && e.ctrlKey);
15952 this.lastSelection = item;
15955 if(!this.tickable){
15956 e.preventDefault();
15964 * Get the number of selected nodes.
15967 getSelectionCount : function(){
15968 return this.selections.length;
15972 * Get the currently selected nodes.
15973 * @return {Array} An array of HTMLElements
15975 getSelectedNodes : function(){
15976 return this.selections;
15980 * Get the indexes of the selected nodes.
15983 getSelectedIndexes : function(){
15984 var indexes = [], s = this.selections;
15985 for(var i = 0, len = s.length; i < len; i++){
15986 indexes.push(s[i].nodeIndex);
15992 * Clear all selections
15993 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15995 clearSelections : function(suppressEvent){
15996 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15997 this.cmp.elements = this.selections;
15998 this.cmp.removeClass(this.selectedClass);
15999 this.selections = [];
16000 if(!suppressEvent){
16001 this.fireEvent("selectionchange", this, this.selections);
16007 * Returns true if the passed node is selected
16008 * @param {HTMLElement/Number} node The node or node index
16009 * @return {Boolean}
16011 isSelected : function(node){
16012 var s = this.selections;
16016 node = this.getNode(node);
16017 return s.indexOf(node) !== -1;
16022 * @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
16023 * @param {Boolean} keepExisting (optional) true to keep existing selections
16024 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16026 select : function(nodeInfo, keepExisting, suppressEvent){
16027 if(nodeInfo instanceof Array){
16029 this.clearSelections(true);
16031 for(var i = 0, len = nodeInfo.length; i < len; i++){
16032 this.select(nodeInfo[i], true, true);
16036 var node = this.getNode(nodeInfo);
16037 if(!node || this.isSelected(node)){
16038 return; // already selected.
16041 this.clearSelections(true);
16044 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16045 Roo.fly(node).addClass(this.selectedClass);
16046 this.selections.push(node);
16047 if(!suppressEvent){
16048 this.fireEvent("selectionchange", this, this.selections);
16056 * @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
16057 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16058 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16060 unselect : function(nodeInfo, keepExisting, suppressEvent)
16062 if(nodeInfo instanceof Array){
16063 Roo.each(this.selections, function(s) {
16064 this.unselect(s, nodeInfo);
16068 var node = this.getNode(nodeInfo);
16069 if(!node || !this.isSelected(node)){
16070 //Roo.log("not selected");
16071 return; // not selected.
16075 Roo.each(this.selections, function(s) {
16077 Roo.fly(node).removeClass(this.selectedClass);
16084 this.selections= ns;
16085 this.fireEvent("selectionchange", this, this.selections);
16089 * Gets a template node.
16090 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16091 * @return {HTMLElement} The node or null if it wasn't found
16093 getNode : function(nodeInfo){
16094 if(typeof nodeInfo == "string"){
16095 return document.getElementById(nodeInfo);
16096 }else if(typeof nodeInfo == "number"){
16097 return this.nodes[nodeInfo];
16103 * Gets a range template nodes.
16104 * @param {Number} startIndex
16105 * @param {Number} endIndex
16106 * @return {Array} An array of nodes
16108 getNodes : function(start, end){
16109 var ns = this.nodes;
16110 start = start || 0;
16111 end = typeof end == "undefined" ? ns.length - 1 : end;
16114 for(var i = start; i <= end; i++){
16118 for(var i = start; i >= end; i--){
16126 * Finds the index of the passed node
16127 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16128 * @return {Number} The index of the node or -1
16130 indexOf : function(node){
16131 node = this.getNode(node);
16132 if(typeof node.nodeIndex == "number"){
16133 return node.nodeIndex;
16135 var ns = this.nodes;
16136 for(var i = 0, len = ns.length; i < len; i++){
16147 * based on jquery fullcalendar
16151 Roo.bootstrap = Roo.bootstrap || {};
16153 * @class Roo.bootstrap.Calendar
16154 * @extends Roo.bootstrap.Component
16155 * Bootstrap Calendar class
16156 * @cfg {Boolean} loadMask (true|false) default false
16157 * @cfg {Object} header generate the user specific header of the calendar, default false
16160 * Create a new Container
16161 * @param {Object} config The config object
16166 Roo.bootstrap.Calendar = function(config){
16167 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16171 * Fires when a date is selected
16172 * @param {DatePicker} this
16173 * @param {Date} date The selected date
16177 * @event monthchange
16178 * Fires when the displayed month changes
16179 * @param {DatePicker} this
16180 * @param {Date} date The selected month
16182 'monthchange': true,
16184 * @event evententer
16185 * Fires when mouse over an event
16186 * @param {Calendar} this
16187 * @param {event} Event
16189 'evententer': true,
16191 * @event eventleave
16192 * Fires when the mouse leaves an
16193 * @param {Calendar} this
16196 'eventleave': true,
16198 * @event eventclick
16199 * Fires when the mouse click an
16200 * @param {Calendar} this
16209 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16212 * @cfg {Number} startDay
16213 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16221 getAutoCreate : function(){
16224 var fc_button = function(name, corner, style, content ) {
16225 return Roo.apply({},{
16227 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16229 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16232 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16243 style : 'width:100%',
16250 cls : 'fc-header-left',
16252 fc_button('prev', 'left', 'arrow', '‹' ),
16253 fc_button('next', 'right', 'arrow', '›' ),
16254 { tag: 'span', cls: 'fc-header-space' },
16255 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16263 cls : 'fc-header-center',
16267 cls: 'fc-header-title',
16270 html : 'month / year'
16278 cls : 'fc-header-right',
16280 /* fc_button('month', 'left', '', 'month' ),
16281 fc_button('week', '', '', 'week' ),
16282 fc_button('day', 'right', '', 'day' )
16294 header = this.header;
16297 var cal_heads = function() {
16299 // fixme - handle this.
16301 for (var i =0; i < Date.dayNames.length; i++) {
16302 var d = Date.dayNames[i];
16305 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16306 html : d.substring(0,3)
16310 ret[0].cls += ' fc-first';
16311 ret[6].cls += ' fc-last';
16314 var cal_cell = function(n) {
16317 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16322 cls: 'fc-day-number',
16326 cls: 'fc-day-content',
16330 style: 'position: relative;' // height: 17px;
16342 var cal_rows = function() {
16345 for (var r = 0; r < 6; r++) {
16352 for (var i =0; i < Date.dayNames.length; i++) {
16353 var d = Date.dayNames[i];
16354 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16357 row.cn[0].cls+=' fc-first';
16358 row.cn[0].cn[0].style = 'min-height:90px';
16359 row.cn[6].cls+=' fc-last';
16363 ret[0].cls += ' fc-first';
16364 ret[4].cls += ' fc-prev-last';
16365 ret[5].cls += ' fc-last';
16372 cls: 'fc-border-separate',
16373 style : 'width:100%',
16381 cls : 'fc-first fc-last',
16399 cls : 'fc-content',
16400 style : "position: relative;",
16403 cls : 'fc-view fc-view-month fc-grid',
16404 style : 'position: relative',
16405 unselectable : 'on',
16408 cls : 'fc-event-container',
16409 style : 'position:absolute;z-index:8;top:0;left:0;'
16427 initEvents : function()
16430 throw "can not find store for calendar";
16436 style: "text-align:center",
16440 style: "background-color:white;width:50%;margin:250 auto",
16444 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16455 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16457 var size = this.el.select('.fc-content', true).first().getSize();
16458 this.maskEl.setSize(size.width, size.height);
16459 this.maskEl.enableDisplayMode("block");
16460 if(!this.loadMask){
16461 this.maskEl.hide();
16464 this.store = Roo.factory(this.store, Roo.data);
16465 this.store.on('load', this.onLoad, this);
16466 this.store.on('beforeload', this.onBeforeLoad, this);
16470 this.cells = this.el.select('.fc-day',true);
16471 //Roo.log(this.cells);
16472 this.textNodes = this.el.query('.fc-day-number');
16473 this.cells.addClassOnOver('fc-state-hover');
16475 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16476 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16477 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16478 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16480 this.on('monthchange', this.onMonthChange, this);
16482 this.update(new Date().clearTime());
16485 resize : function() {
16486 var sz = this.el.getSize();
16488 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16489 this.el.select('.fc-day-content div',true).setHeight(34);
16494 showPrevMonth : function(e){
16495 this.update(this.activeDate.add("mo", -1));
16497 showToday : function(e){
16498 this.update(new Date().clearTime());
16501 showNextMonth : function(e){
16502 this.update(this.activeDate.add("mo", 1));
16506 showPrevYear : function(){
16507 this.update(this.activeDate.add("y", -1));
16511 showNextYear : function(){
16512 this.update(this.activeDate.add("y", 1));
16517 update : function(date)
16519 var vd = this.activeDate;
16520 this.activeDate = date;
16521 // if(vd && this.el){
16522 // var t = date.getTime();
16523 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16524 // Roo.log('using add remove');
16526 // this.fireEvent('monthchange', this, date);
16528 // this.cells.removeClass("fc-state-highlight");
16529 // this.cells.each(function(c){
16530 // if(c.dateValue == t){
16531 // c.addClass("fc-state-highlight");
16532 // setTimeout(function(){
16533 // try{c.dom.firstChild.focus();}catch(e){}
16543 var days = date.getDaysInMonth();
16545 var firstOfMonth = date.getFirstDateOfMonth();
16546 var startingPos = firstOfMonth.getDay()-this.startDay;
16548 if(startingPos < this.startDay){
16552 var pm = date.add(Date.MONTH, -1);
16553 var prevStart = pm.getDaysInMonth()-startingPos;
16555 this.cells = this.el.select('.fc-day',true);
16556 this.textNodes = this.el.query('.fc-day-number');
16557 this.cells.addClassOnOver('fc-state-hover');
16559 var cells = this.cells.elements;
16560 var textEls = this.textNodes;
16562 Roo.each(cells, function(cell){
16563 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16566 days += startingPos;
16568 // convert everything to numbers so it's fast
16569 var day = 86400000;
16570 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16573 //Roo.log(prevStart);
16575 var today = new Date().clearTime().getTime();
16576 var sel = date.clearTime().getTime();
16577 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16578 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16579 var ddMatch = this.disabledDatesRE;
16580 var ddText = this.disabledDatesText;
16581 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16582 var ddaysText = this.disabledDaysText;
16583 var format = this.format;
16585 var setCellClass = function(cal, cell){
16589 //Roo.log('set Cell Class');
16591 var t = d.getTime();
16595 cell.dateValue = t;
16597 cell.className += " fc-today";
16598 cell.className += " fc-state-highlight";
16599 cell.title = cal.todayText;
16602 // disable highlight in other month..
16603 //cell.className += " fc-state-highlight";
16608 cell.className = " fc-state-disabled";
16609 cell.title = cal.minText;
16613 cell.className = " fc-state-disabled";
16614 cell.title = cal.maxText;
16618 if(ddays.indexOf(d.getDay()) != -1){
16619 cell.title = ddaysText;
16620 cell.className = " fc-state-disabled";
16623 if(ddMatch && format){
16624 var fvalue = d.dateFormat(format);
16625 if(ddMatch.test(fvalue)){
16626 cell.title = ddText.replace("%0", fvalue);
16627 cell.className = " fc-state-disabled";
16631 if (!cell.initialClassName) {
16632 cell.initialClassName = cell.dom.className;
16635 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16640 for(; i < startingPos; i++) {
16641 textEls[i].innerHTML = (++prevStart);
16642 d.setDate(d.getDate()+1);
16644 cells[i].className = "fc-past fc-other-month";
16645 setCellClass(this, cells[i]);
16650 for(; i < days; i++){
16651 intDay = i - startingPos + 1;
16652 textEls[i].innerHTML = (intDay);
16653 d.setDate(d.getDate()+1);
16655 cells[i].className = ''; // "x-date-active";
16656 setCellClass(this, cells[i]);
16660 for(; i < 42; i++) {
16661 textEls[i].innerHTML = (++extraDays);
16662 d.setDate(d.getDate()+1);
16664 cells[i].className = "fc-future fc-other-month";
16665 setCellClass(this, cells[i]);
16668 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16670 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16672 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16673 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16675 if(totalRows != 6){
16676 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16677 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16680 this.fireEvent('monthchange', this, date);
16684 if(!this.internalRender){
16685 var main = this.el.dom.firstChild;
16686 var w = main.offsetWidth;
16687 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16688 Roo.fly(main).setWidth(w);
16689 this.internalRender = true;
16690 // opera does not respect the auto grow header center column
16691 // then, after it gets a width opera refuses to recalculate
16692 // without a second pass
16693 if(Roo.isOpera && !this.secondPass){
16694 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16695 this.secondPass = true;
16696 this.update.defer(10, this, [date]);
16703 findCell : function(dt) {
16704 dt = dt.clearTime().getTime();
16706 this.cells.each(function(c){
16707 //Roo.log("check " +c.dateValue + '?=' + dt);
16708 if(c.dateValue == dt){
16718 findCells : function(ev) {
16719 var s = ev.start.clone().clearTime().getTime();
16721 var e= ev.end.clone().clearTime().getTime();
16724 this.cells.each(function(c){
16725 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16727 if(c.dateValue > e){
16730 if(c.dateValue < s){
16739 // findBestRow: function(cells)
16743 // for (var i =0 ; i < cells.length;i++) {
16744 // ret = Math.max(cells[i].rows || 0,ret);
16751 addItem : function(ev)
16753 // look for vertical location slot in
16754 var cells = this.findCells(ev);
16756 // ev.row = this.findBestRow(cells);
16758 // work out the location.
16762 for(var i =0; i < cells.length; i++) {
16764 cells[i].row = cells[0].row;
16767 cells[i].row = cells[i].row + 1;
16777 if (crow.start.getY() == cells[i].getY()) {
16779 crow.end = cells[i];
16796 cells[0].events.push(ev);
16798 this.calevents.push(ev);
16801 clearEvents: function() {
16803 if(!this.calevents){
16807 Roo.each(this.cells.elements, function(c){
16813 Roo.each(this.calevents, function(e) {
16814 Roo.each(e.els, function(el) {
16815 el.un('mouseenter' ,this.onEventEnter, this);
16816 el.un('mouseleave' ,this.onEventLeave, this);
16821 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16827 renderEvents: function()
16831 this.cells.each(function(c) {
16840 if(c.row != c.events.length){
16841 r = 4 - (4 - (c.row - c.events.length));
16844 c.events = ev.slice(0, r);
16845 c.more = ev.slice(r);
16847 if(c.more.length && c.more.length == 1){
16848 c.events.push(c.more.pop());
16851 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16855 this.cells.each(function(c) {
16857 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16860 for (var e = 0; e < c.events.length; e++){
16861 var ev = c.events[e];
16862 var rows = ev.rows;
16864 for(var i = 0; i < rows.length; i++) {
16866 // how many rows should it span..
16869 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16870 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16872 unselectable : "on",
16875 cls: 'fc-event-inner',
16879 // cls: 'fc-event-time',
16880 // html : cells.length > 1 ? '' : ev.time
16884 cls: 'fc-event-title',
16885 html : String.format('{0}', ev.title)
16892 cls: 'ui-resizable-handle ui-resizable-e',
16893 html : '  '
16900 cfg.cls += ' fc-event-start';
16902 if ((i+1) == rows.length) {
16903 cfg.cls += ' fc-event-end';
16906 var ctr = _this.el.select('.fc-event-container',true).first();
16907 var cg = ctr.createChild(cfg);
16909 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16910 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16912 var r = (c.more.length) ? 1 : 0;
16913 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16914 cg.setWidth(ebox.right - sbox.x -2);
16916 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16917 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16918 cg.on('click', _this.onEventClick, _this, ev);
16929 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16930 style : 'position: absolute',
16931 unselectable : "on",
16934 cls: 'fc-event-inner',
16938 cls: 'fc-event-title',
16946 cls: 'ui-resizable-handle ui-resizable-e',
16947 html : '  '
16953 var ctr = _this.el.select('.fc-event-container',true).first();
16954 var cg = ctr.createChild(cfg);
16956 var sbox = c.select('.fc-day-content',true).first().getBox();
16957 var ebox = c.select('.fc-day-content',true).first().getBox();
16959 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16960 cg.setWidth(ebox.right - sbox.x -2);
16962 cg.on('click', _this.onMoreEventClick, _this, c.more);
16972 onEventEnter: function (e, el,event,d) {
16973 this.fireEvent('evententer', this, el, event);
16976 onEventLeave: function (e, el,event,d) {
16977 this.fireEvent('eventleave', this, el, event);
16980 onEventClick: function (e, el,event,d) {
16981 this.fireEvent('eventclick', this, el, event);
16984 onMonthChange: function () {
16988 onMoreEventClick: function(e, el, more)
16992 this.calpopover.placement = 'right';
16993 this.calpopover.setTitle('More');
16995 this.calpopover.setContent('');
16997 var ctr = this.calpopover.el.select('.popover-content', true).first();
16999 Roo.each(more, function(m){
17001 cls : 'fc-event-hori fc-event-draggable',
17004 var cg = ctr.createChild(cfg);
17006 cg.on('click', _this.onEventClick, _this, m);
17009 this.calpopover.show(el);
17014 onLoad: function ()
17016 this.calevents = [];
17019 if(this.store.getCount() > 0){
17020 this.store.data.each(function(d){
17023 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17024 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17025 time : d.data.start_time,
17026 title : d.data.title,
17027 description : d.data.description,
17028 venue : d.data.venue
17033 this.renderEvents();
17035 if(this.calevents.length && this.loadMask){
17036 this.maskEl.hide();
17040 onBeforeLoad: function()
17042 this.clearEvents();
17044 this.maskEl.show();
17058 * @class Roo.bootstrap.Popover
17059 * @extends Roo.bootstrap.Component
17060 * Bootstrap Popover class
17061 * @cfg {String} html contents of the popover (or false to use children..)
17062 * @cfg {String} title of popover (or false to hide)
17063 * @cfg {String} placement how it is placed
17064 * @cfg {String} trigger click || hover (or false to trigger manually)
17065 * @cfg {String} over what (parent or false to trigger manually.)
17066 * @cfg {Number} delay - delay before showing
17069 * Create a new Popover
17070 * @param {Object} config The config object
17073 Roo.bootstrap.Popover = function(config){
17074 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17080 * After the popover show
17082 * @param {Roo.bootstrap.Popover} this
17087 * After the popover hide
17089 * @param {Roo.bootstrap.Popover} this
17095 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17097 title: 'Fill in a title',
17100 placement : 'right',
17101 trigger : 'hover', // hover
17107 can_build_overlaid : false,
17109 getChildContainer : function()
17111 return this.el.select('.popover-content',true).first();
17114 getAutoCreate : function(){
17117 cls : 'popover roo-dynamic',
17118 style: 'display:block',
17124 cls : 'popover-inner',
17128 cls: 'popover-title',
17132 cls : 'popover-content',
17143 setTitle: function(str)
17146 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17148 setContent: function(str)
17151 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17153 // as it get's added to the bottom of the page.
17154 onRender : function(ct, position)
17156 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17158 var cfg = Roo.apply({}, this.getAutoCreate());
17162 cfg.cls += ' ' + this.cls;
17165 cfg.style = this.style;
17167 //Roo.log("adding to ");
17168 this.el = Roo.get(document.body).createChild(cfg, position);
17169 // Roo.log(this.el);
17174 initEvents : function()
17176 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17177 this.el.enableDisplayMode('block');
17179 if (this.over === false) {
17182 if (this.triggers === false) {
17185 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17186 var triggers = this.trigger ? this.trigger.split(' ') : [];
17187 Roo.each(triggers, function(trigger) {
17189 if (trigger == 'click') {
17190 on_el.on('click', this.toggle, this);
17191 } else if (trigger != 'manual') {
17192 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17193 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17195 on_el.on(eventIn ,this.enter, this);
17196 on_el.on(eventOut, this.leave, this);
17207 toggle : function () {
17208 this.hoverState == 'in' ? this.leave() : this.enter();
17211 enter : function () {
17213 clearTimeout(this.timeout);
17215 this.hoverState = 'in';
17217 if (!this.delay || !this.delay.show) {
17222 this.timeout = setTimeout(function () {
17223 if (_t.hoverState == 'in') {
17226 }, this.delay.show)
17229 leave : function() {
17230 clearTimeout(this.timeout);
17232 this.hoverState = 'out';
17234 if (!this.delay || !this.delay.hide) {
17239 this.timeout = setTimeout(function () {
17240 if (_t.hoverState == 'out') {
17243 }, this.delay.hide)
17246 show : function (on_el)
17249 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17253 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17254 if (this.html !== false) {
17255 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17257 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17258 if (!this.title.length) {
17259 this.el.select('.popover-title',true).hide();
17262 var placement = typeof this.placement == 'function' ?
17263 this.placement.call(this, this.el, on_el) :
17266 var autoToken = /\s?auto?\s?/i;
17267 var autoPlace = autoToken.test(placement);
17269 placement = placement.replace(autoToken, '') || 'top';
17273 //this.el.setXY([0,0]);
17275 this.el.dom.style.display='block';
17276 this.el.addClass(placement);
17278 //this.el.appendTo(on_el);
17280 var p = this.getPosition();
17281 var box = this.el.getBox();
17286 var align = Roo.bootstrap.Popover.alignment[placement];
17287 this.el.alignTo(on_el, align[0],align[1]);
17288 //var arrow = this.el.select('.arrow',true).first();
17289 //arrow.set(align[2],
17291 this.el.addClass('in');
17294 if (this.el.hasClass('fade')) {
17298 this.hoverState = 'in';
17300 this.fireEvent('show', this);
17305 this.el.setXY([0,0]);
17306 this.el.removeClass('in');
17308 this.hoverState = null;
17310 this.fireEvent('hide', this);
17315 Roo.bootstrap.Popover.alignment = {
17316 'left' : ['r-l', [-10,0], 'right'],
17317 'right' : ['l-r', [10,0], 'left'],
17318 'bottom' : ['t-b', [0,10], 'top'],
17319 'top' : [ 'b-t', [0,-10], 'bottom']
17330 * @class Roo.bootstrap.Progress
17331 * @extends Roo.bootstrap.Component
17332 * Bootstrap Progress class
17333 * @cfg {Boolean} striped striped of the progress bar
17334 * @cfg {Boolean} active animated of the progress bar
17338 * Create a new Progress
17339 * @param {Object} config The config object
17342 Roo.bootstrap.Progress = function(config){
17343 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17346 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17351 getAutoCreate : function(){
17359 cfg.cls += ' progress-striped';
17363 cfg.cls += ' active';
17382 * @class Roo.bootstrap.ProgressBar
17383 * @extends Roo.bootstrap.Component
17384 * Bootstrap ProgressBar class
17385 * @cfg {Number} aria_valuenow aria-value now
17386 * @cfg {Number} aria_valuemin aria-value min
17387 * @cfg {Number} aria_valuemax aria-value max
17388 * @cfg {String} label label for the progress bar
17389 * @cfg {String} panel (success | info | warning | danger )
17390 * @cfg {String} role role of the progress bar
17391 * @cfg {String} sr_only text
17395 * Create a new ProgressBar
17396 * @param {Object} config The config object
17399 Roo.bootstrap.ProgressBar = function(config){
17400 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17403 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17407 aria_valuemax : 100,
17413 getAutoCreate : function()
17418 cls: 'progress-bar',
17419 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17431 cfg.role = this.role;
17434 if(this.aria_valuenow){
17435 cfg['aria-valuenow'] = this.aria_valuenow;
17438 if(this.aria_valuemin){
17439 cfg['aria-valuemin'] = this.aria_valuemin;
17442 if(this.aria_valuemax){
17443 cfg['aria-valuemax'] = this.aria_valuemax;
17446 if(this.label && !this.sr_only){
17447 cfg.html = this.label;
17451 cfg.cls += ' progress-bar-' + this.panel;
17457 update : function(aria_valuenow)
17459 this.aria_valuenow = aria_valuenow;
17461 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17476 * @class Roo.bootstrap.TabGroup
17477 * @extends Roo.bootstrap.Column
17478 * Bootstrap Column class
17479 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17480 * @cfg {Boolean} carousel true to make the group behave like a carousel
17481 * @cfg {Boolean} bullets show bullets for the panels
17482 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17483 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17484 * @cfg {Boolean} showarrow (true|false) show arrow default true
17487 * Create a new TabGroup
17488 * @param {Object} config The config object
17491 Roo.bootstrap.TabGroup = function(config){
17492 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17494 this.navId = Roo.id();
17497 Roo.bootstrap.TabGroup.register(this);
17501 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17504 transition : false,
17509 slideOnTouch : false,
17512 getAutoCreate : function()
17514 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17516 cfg.cls += ' tab-content';
17518 if (this.carousel) {
17519 cfg.cls += ' carousel slide';
17522 cls : 'carousel-inner',
17526 if(this.bullets && !Roo.isTouch){
17529 cls : 'carousel-bullets',
17533 if(this.bullets_cls){
17534 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17541 cfg.cn[0].cn.push(bullets);
17544 if(this.showarrow){
17545 cfg.cn[0].cn.push({
17547 class : 'carousel-arrow',
17551 class : 'carousel-prev',
17555 class : 'fa fa-chevron-left'
17561 class : 'carousel-next',
17565 class : 'fa fa-chevron-right'
17578 initEvents: function()
17580 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17581 // this.el.on("touchstart", this.onTouchStart, this);
17584 if(this.autoslide){
17587 this.slideFn = window.setInterval(function() {
17588 _this.showPanelNext();
17592 if(this.showarrow){
17593 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17594 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17600 // onTouchStart : function(e, el, o)
17602 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17606 // this.showPanelNext();
17610 getChildContainer : function()
17612 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17616 * register a Navigation item
17617 * @param {Roo.bootstrap.NavItem} the navitem to add
17619 register : function(item)
17621 this.tabs.push( item);
17622 item.navId = this.navId; // not really needed..
17627 getActivePanel : function()
17630 Roo.each(this.tabs, function(t) {
17640 getPanelByName : function(n)
17643 Roo.each(this.tabs, function(t) {
17644 if (t.tabId == n) {
17652 indexOfPanel : function(p)
17655 Roo.each(this.tabs, function(t,i) {
17656 if (t.tabId == p.tabId) {
17665 * show a specific panel
17666 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17667 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17669 showPanel : function (pan)
17671 if(this.transition || typeof(pan) == 'undefined'){
17672 Roo.log("waiting for the transitionend");
17676 if (typeof(pan) == 'number') {
17677 pan = this.tabs[pan];
17680 if (typeof(pan) == 'string') {
17681 pan = this.getPanelByName(pan);
17684 var cur = this.getActivePanel();
17687 Roo.log('pan or acitve pan is undefined');
17691 if (pan.tabId == this.getActivePanel().tabId) {
17695 if (false === cur.fireEvent('beforedeactivate')) {
17699 if(this.bullets > 0 && !Roo.isTouch){
17700 this.setActiveBullet(this.indexOfPanel(pan));
17703 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17705 this.transition = true;
17706 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17707 var lr = dir == 'next' ? 'left' : 'right';
17708 pan.el.addClass(dir); // or prev
17709 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17710 cur.el.addClass(lr); // or right
17711 pan.el.addClass(lr);
17714 cur.el.on('transitionend', function() {
17715 Roo.log("trans end?");
17717 pan.el.removeClass([lr,dir]);
17718 pan.setActive(true);
17720 cur.el.removeClass([lr]);
17721 cur.setActive(false);
17723 _this.transition = false;
17725 }, this, { single: true } );
17730 cur.setActive(false);
17731 pan.setActive(true);
17736 showPanelNext : function()
17738 var i = this.indexOfPanel(this.getActivePanel());
17740 if (i >= this.tabs.length - 1 && !this.autoslide) {
17744 if (i >= this.tabs.length - 1 && this.autoslide) {
17748 this.showPanel(this.tabs[i+1]);
17751 showPanelPrev : function()
17753 var i = this.indexOfPanel(this.getActivePanel());
17755 if (i < 1 && !this.autoslide) {
17759 if (i < 1 && this.autoslide) {
17760 i = this.tabs.length;
17763 this.showPanel(this.tabs[i-1]);
17767 addBullet: function()
17769 if(!this.bullets || Roo.isTouch){
17772 var ctr = this.el.select('.carousel-bullets',true).first();
17773 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17774 var bullet = ctr.createChild({
17775 cls : 'bullet bullet-' + i
17776 },ctr.dom.lastChild);
17781 bullet.on('click', (function(e, el, o, ii, t){
17783 e.preventDefault();
17785 this.showPanel(ii);
17787 if(this.autoslide && this.slideFn){
17788 clearInterval(this.slideFn);
17789 this.slideFn = window.setInterval(function() {
17790 _this.showPanelNext();
17794 }).createDelegate(this, [i, bullet], true));
17799 setActiveBullet : function(i)
17805 Roo.each(this.el.select('.bullet', true).elements, function(el){
17806 el.removeClass('selected');
17809 var bullet = this.el.select('.bullet-' + i, true).first();
17815 bullet.addClass('selected');
17826 Roo.apply(Roo.bootstrap.TabGroup, {
17830 * register a Navigation Group
17831 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17833 register : function(navgrp)
17835 this.groups[navgrp.navId] = navgrp;
17839 * fetch a Navigation Group based on the navigation ID
17840 * if one does not exist , it will get created.
17841 * @param {string} the navgroup to add
17842 * @returns {Roo.bootstrap.NavGroup} the navgroup
17844 get: function(navId) {
17845 if (typeof(this.groups[navId]) == 'undefined') {
17846 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17848 return this.groups[navId] ;
17863 * @class Roo.bootstrap.TabPanel
17864 * @extends Roo.bootstrap.Component
17865 * Bootstrap TabPanel class
17866 * @cfg {Boolean} active panel active
17867 * @cfg {String} html panel content
17868 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17869 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17870 * @cfg {String} href click to link..
17874 * Create a new TabPanel
17875 * @param {Object} config The config object
17878 Roo.bootstrap.TabPanel = function(config){
17879 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17883 * Fires when the active status changes
17884 * @param {Roo.bootstrap.TabPanel} this
17885 * @param {Boolean} state the new state
17890 * @event beforedeactivate
17891 * Fires before a tab is de-activated - can be used to do validation on a form.
17892 * @param {Roo.bootstrap.TabPanel} this
17893 * @return {Boolean} false if there is an error
17896 'beforedeactivate': true
17899 this.tabId = this.tabId || Roo.id();
17903 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17911 getAutoCreate : function(){
17914 // item is needed for carousel - not sure if it has any effect otherwise
17915 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17916 html: this.html || ''
17920 cfg.cls += ' active';
17924 cfg.tabId = this.tabId;
17931 initEvents: function()
17933 var p = this.parent();
17935 this.navId = this.navId || p.navId;
17937 if (typeof(this.navId) != 'undefined') {
17938 // not really needed.. but just in case.. parent should be a NavGroup.
17939 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17943 var i = tg.tabs.length - 1;
17945 if(this.active && tg.bullets > 0 && i < tg.bullets){
17946 tg.setActiveBullet(i);
17950 this.el.on('click', this.onClick, this);
17953 this.el.on("touchstart", this.onTouchStart, this);
17954 this.el.on("touchmove", this.onTouchMove, this);
17955 this.el.on("touchend", this.onTouchEnd, this);
17960 onRender : function(ct, position)
17962 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17965 setActive : function(state)
17967 Roo.log("panel - set active " + this.tabId + "=" + state);
17969 this.active = state;
17971 this.el.removeClass('active');
17973 } else if (!this.el.hasClass('active')) {
17974 this.el.addClass('active');
17977 this.fireEvent('changed', this, state);
17980 onClick : function(e)
17982 e.preventDefault();
17984 if(!this.href.length){
17988 window.location.href = this.href;
17997 onTouchStart : function(e)
17999 this.swiping = false;
18001 this.startX = e.browserEvent.touches[0].clientX;
18002 this.startY = e.browserEvent.touches[0].clientY;
18005 onTouchMove : function(e)
18007 this.swiping = true;
18009 this.endX = e.browserEvent.touches[0].clientX;
18010 this.endY = e.browserEvent.touches[0].clientY;
18013 onTouchEnd : function(e)
18020 var tabGroup = this.parent();
18022 if(this.endX > this.startX){ // swiping right
18023 tabGroup.showPanelPrev();
18027 if(this.startX > this.endX){ // swiping left
18028 tabGroup.showPanelNext();
18047 * @class Roo.bootstrap.DateField
18048 * @extends Roo.bootstrap.Input
18049 * Bootstrap DateField class
18050 * @cfg {Number} weekStart default 0
18051 * @cfg {String} viewMode default empty, (months|years)
18052 * @cfg {String} minViewMode default empty, (months|years)
18053 * @cfg {Number} startDate default -Infinity
18054 * @cfg {Number} endDate default Infinity
18055 * @cfg {Boolean} todayHighlight default false
18056 * @cfg {Boolean} todayBtn default false
18057 * @cfg {Boolean} calendarWeeks default false
18058 * @cfg {Object} daysOfWeekDisabled default empty
18059 * @cfg {Boolean} singleMode default false (true | false)
18061 * @cfg {Boolean} keyboardNavigation default true
18062 * @cfg {String} language default en
18065 * Create a new DateField
18066 * @param {Object} config The config object
18069 Roo.bootstrap.DateField = function(config){
18070 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18074 * Fires when this field show.
18075 * @param {Roo.bootstrap.DateField} this
18076 * @param {Mixed} date The date value
18081 * Fires when this field hide.
18082 * @param {Roo.bootstrap.DateField} this
18083 * @param {Mixed} date The date value
18088 * Fires when select a date.
18089 * @param {Roo.bootstrap.DateField} this
18090 * @param {Mixed} date The date value
18094 * @event beforeselect
18095 * Fires when before select a date.
18096 * @param {Roo.bootstrap.DateField} this
18097 * @param {Mixed} date The date value
18099 beforeselect : true
18103 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18106 * @cfg {String} format
18107 * The default date format string which can be overriden for localization support. The format must be
18108 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18112 * @cfg {String} altFormats
18113 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18114 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18116 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18124 todayHighlight : false,
18130 keyboardNavigation: true,
18132 calendarWeeks: false,
18134 startDate: -Infinity,
18138 daysOfWeekDisabled: [],
18142 singleMode : false,
18144 UTCDate: function()
18146 return new Date(Date.UTC.apply(Date, arguments));
18149 UTCToday: function()
18151 var today = new Date();
18152 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18155 getDate: function() {
18156 var d = this.getUTCDate();
18157 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18160 getUTCDate: function() {
18164 setDate: function(d) {
18165 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18168 setUTCDate: function(d) {
18170 this.setValue(this.formatDate(this.date));
18173 onRender: function(ct, position)
18176 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18178 this.language = this.language || 'en';
18179 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18180 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18182 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18183 this.format = this.format || 'm/d/y';
18184 this.isInline = false;
18185 this.isInput = true;
18186 this.component = this.el.select('.add-on', true).first() || false;
18187 this.component = (this.component && this.component.length === 0) ? false : this.component;
18188 this.hasInput = this.component && this.inputEl().length;
18190 if (typeof(this.minViewMode === 'string')) {
18191 switch (this.minViewMode) {
18193 this.minViewMode = 1;
18196 this.minViewMode = 2;
18199 this.minViewMode = 0;
18204 if (typeof(this.viewMode === 'string')) {
18205 switch (this.viewMode) {
18218 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18220 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18222 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18224 this.picker().on('mousedown', this.onMousedown, this);
18225 this.picker().on('click', this.onClick, this);
18227 this.picker().addClass('datepicker-dropdown');
18229 this.startViewMode = this.viewMode;
18231 if(this.singleMode){
18232 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18233 v.setVisibilityMode(Roo.Element.DISPLAY);
18237 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18238 v.setStyle('width', '189px');
18242 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18243 if(!this.calendarWeeks){
18248 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18249 v.attr('colspan', function(i, val){
18250 return parseInt(val) + 1;
18255 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18257 this.setStartDate(this.startDate);
18258 this.setEndDate(this.endDate);
18260 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18267 if(this.isInline) {
18272 picker : function()
18274 return this.pickerEl;
18275 // return this.el.select('.datepicker', true).first();
18278 fillDow: function()
18280 var dowCnt = this.weekStart;
18289 if(this.calendarWeeks){
18297 while (dowCnt < this.weekStart + 7) {
18301 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18305 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18308 fillMonths: function()
18311 var months = this.picker().select('>.datepicker-months td', true).first();
18313 months.dom.innerHTML = '';
18319 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18322 months.createChild(month);
18329 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;
18331 if (this.date < this.startDate) {
18332 this.viewDate = new Date(this.startDate);
18333 } else if (this.date > this.endDate) {
18334 this.viewDate = new Date(this.endDate);
18336 this.viewDate = new Date(this.date);
18344 var d = new Date(this.viewDate),
18345 year = d.getUTCFullYear(),
18346 month = d.getUTCMonth(),
18347 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18348 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18349 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18350 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18351 currentDate = this.date && this.date.valueOf(),
18352 today = this.UTCToday();
18354 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18356 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18358 // this.picker.select('>tfoot th.today').
18359 // .text(dates[this.language].today)
18360 // .toggle(this.todayBtn !== false);
18362 this.updateNavArrows();
18365 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18367 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18369 prevMonth.setUTCDate(day);
18371 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18373 var nextMonth = new Date(prevMonth);
18375 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18377 nextMonth = nextMonth.valueOf();
18379 var fillMonths = false;
18381 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18383 while(prevMonth.valueOf() < nextMonth) {
18386 if (prevMonth.getUTCDay() === this.weekStart) {
18388 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18396 if(this.calendarWeeks){
18397 // ISO 8601: First week contains first thursday.
18398 // ISO also states week starts on Monday, but we can be more abstract here.
18400 // Start of current week: based on weekstart/current date
18401 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18402 // Thursday of this week
18403 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18404 // First Thursday of year, year from thursday
18405 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18406 // Calendar week: ms between thursdays, div ms per day, div 7 days
18407 calWeek = (th - yth) / 864e5 / 7 + 1;
18409 fillMonths.cn.push({
18417 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18419 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18422 if (this.todayHighlight &&
18423 prevMonth.getUTCFullYear() == today.getFullYear() &&
18424 prevMonth.getUTCMonth() == today.getMonth() &&
18425 prevMonth.getUTCDate() == today.getDate()) {
18426 clsName += ' today';
18429 if (currentDate && prevMonth.valueOf() === currentDate) {
18430 clsName += ' active';
18433 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18434 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18435 clsName += ' disabled';
18438 fillMonths.cn.push({
18440 cls: 'day ' + clsName,
18441 html: prevMonth.getDate()
18444 prevMonth.setDate(prevMonth.getDate()+1);
18447 var currentYear = this.date && this.date.getUTCFullYear();
18448 var currentMonth = this.date && this.date.getUTCMonth();
18450 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18452 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18453 v.removeClass('active');
18455 if(currentYear === year && k === currentMonth){
18456 v.addClass('active');
18459 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18460 v.addClass('disabled');
18466 year = parseInt(year/10, 10) * 10;
18468 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18470 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18473 for (var i = -1; i < 11; i++) {
18474 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18476 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18484 showMode: function(dir)
18487 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18490 Roo.each(this.picker().select('>div',true).elements, function(v){
18491 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18494 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18499 if(this.isInline) {
18503 this.picker().removeClass(['bottom', 'top']);
18505 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18507 * place to the top of element!
18511 this.picker().addClass('top');
18512 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18517 this.picker().addClass('bottom');
18519 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18522 parseDate : function(value)
18524 if(!value || value instanceof Date){
18527 var v = Date.parseDate(value, this.format);
18528 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18529 v = Date.parseDate(value, 'Y-m-d');
18531 if(!v && this.altFormats){
18532 if(!this.altFormatsArray){
18533 this.altFormatsArray = this.altFormats.split("|");
18535 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18536 v = Date.parseDate(value, this.altFormatsArray[i]);
18542 formatDate : function(date, fmt)
18544 return (!date || !(date instanceof Date)) ?
18545 date : date.dateFormat(fmt || this.format);
18548 onFocus : function()
18550 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18554 onBlur : function()
18556 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18558 var d = this.inputEl().getValue();
18567 this.picker().show();
18571 this.fireEvent('show', this, this.date);
18576 if(this.isInline) {
18579 this.picker().hide();
18580 this.viewMode = this.startViewMode;
18583 this.fireEvent('hide', this, this.date);
18587 onMousedown: function(e)
18589 e.stopPropagation();
18590 e.preventDefault();
18595 Roo.bootstrap.DateField.superclass.keyup.call(this);
18599 setValue: function(v)
18601 if(this.fireEvent('beforeselect', this, v) !== false){
18602 var d = new Date(this.parseDate(v) ).clearTime();
18604 if(isNaN(d.getTime())){
18605 this.date = this.viewDate = '';
18606 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18610 v = this.formatDate(d);
18612 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18614 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18618 this.fireEvent('select', this, this.date);
18622 getValue: function()
18624 return this.formatDate(this.date);
18627 fireKey: function(e)
18629 if (!this.picker().isVisible()){
18630 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18636 var dateChanged = false,
18638 newDate, newViewDate;
18643 e.preventDefault();
18647 if (!this.keyboardNavigation) {
18650 dir = e.keyCode == 37 ? -1 : 1;
18653 newDate = this.moveYear(this.date, dir);
18654 newViewDate = this.moveYear(this.viewDate, dir);
18655 } else if (e.shiftKey){
18656 newDate = this.moveMonth(this.date, dir);
18657 newViewDate = this.moveMonth(this.viewDate, dir);
18659 newDate = new Date(this.date);
18660 newDate.setUTCDate(this.date.getUTCDate() + dir);
18661 newViewDate = new Date(this.viewDate);
18662 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18664 if (this.dateWithinRange(newDate)){
18665 this.date = newDate;
18666 this.viewDate = newViewDate;
18667 this.setValue(this.formatDate(this.date));
18669 e.preventDefault();
18670 dateChanged = true;
18675 if (!this.keyboardNavigation) {
18678 dir = e.keyCode == 38 ? -1 : 1;
18680 newDate = this.moveYear(this.date, dir);
18681 newViewDate = this.moveYear(this.viewDate, dir);
18682 } else if (e.shiftKey){
18683 newDate = this.moveMonth(this.date, dir);
18684 newViewDate = this.moveMonth(this.viewDate, dir);
18686 newDate = new Date(this.date);
18687 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18688 newViewDate = new Date(this.viewDate);
18689 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18691 if (this.dateWithinRange(newDate)){
18692 this.date = newDate;
18693 this.viewDate = newViewDate;
18694 this.setValue(this.formatDate(this.date));
18696 e.preventDefault();
18697 dateChanged = true;
18701 this.setValue(this.formatDate(this.date));
18703 e.preventDefault();
18706 this.setValue(this.formatDate(this.date));
18720 onClick: function(e)
18722 e.stopPropagation();
18723 e.preventDefault();
18725 var target = e.getTarget();
18727 if(target.nodeName.toLowerCase() === 'i'){
18728 target = Roo.get(target).dom.parentNode;
18731 var nodeName = target.nodeName;
18732 var className = target.className;
18733 var html = target.innerHTML;
18734 //Roo.log(nodeName);
18736 switch(nodeName.toLowerCase()) {
18738 switch(className) {
18744 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18745 switch(this.viewMode){
18747 this.viewDate = this.moveMonth(this.viewDate, dir);
18751 this.viewDate = this.moveYear(this.viewDate, dir);
18757 var date = new Date();
18758 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18760 this.setValue(this.formatDate(this.date));
18767 if (className.indexOf('disabled') < 0) {
18768 this.viewDate.setUTCDate(1);
18769 if (className.indexOf('month') > -1) {
18770 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18772 var year = parseInt(html, 10) || 0;
18773 this.viewDate.setUTCFullYear(year);
18777 if(this.singleMode){
18778 this.setValue(this.formatDate(this.viewDate));
18789 //Roo.log(className);
18790 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18791 var day = parseInt(html, 10) || 1;
18792 var year = this.viewDate.getUTCFullYear(),
18793 month = this.viewDate.getUTCMonth();
18795 if (className.indexOf('old') > -1) {
18802 } else if (className.indexOf('new') > -1) {
18810 //Roo.log([year,month,day]);
18811 this.date = this.UTCDate(year, month, day,0,0,0,0);
18812 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18814 //Roo.log(this.formatDate(this.date));
18815 this.setValue(this.formatDate(this.date));
18822 setStartDate: function(startDate)
18824 this.startDate = startDate || -Infinity;
18825 if (this.startDate !== -Infinity) {
18826 this.startDate = this.parseDate(this.startDate);
18829 this.updateNavArrows();
18832 setEndDate: function(endDate)
18834 this.endDate = endDate || Infinity;
18835 if (this.endDate !== Infinity) {
18836 this.endDate = this.parseDate(this.endDate);
18839 this.updateNavArrows();
18842 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18844 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18845 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18846 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18848 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18849 return parseInt(d, 10);
18852 this.updateNavArrows();
18855 updateNavArrows: function()
18857 if(this.singleMode){
18861 var d = new Date(this.viewDate),
18862 year = d.getUTCFullYear(),
18863 month = d.getUTCMonth();
18865 Roo.each(this.picker().select('.prev', true).elements, function(v){
18867 switch (this.viewMode) {
18870 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18876 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18883 Roo.each(this.picker().select('.next', true).elements, function(v){
18885 switch (this.viewMode) {
18888 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18894 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18902 moveMonth: function(date, dir)
18907 var new_date = new Date(date.valueOf()),
18908 day = new_date.getUTCDate(),
18909 month = new_date.getUTCMonth(),
18910 mag = Math.abs(dir),
18912 dir = dir > 0 ? 1 : -1;
18915 // If going back one month, make sure month is not current month
18916 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18918 return new_date.getUTCMonth() == month;
18920 // If going forward one month, make sure month is as expected
18921 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18923 return new_date.getUTCMonth() != new_month;
18925 new_month = month + dir;
18926 new_date.setUTCMonth(new_month);
18927 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18928 if (new_month < 0 || new_month > 11) {
18929 new_month = (new_month + 12) % 12;
18932 // For magnitudes >1, move one month at a time...
18933 for (var i=0; i<mag; i++) {
18934 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18935 new_date = this.moveMonth(new_date, dir);
18937 // ...then reset the day, keeping it in the new month
18938 new_month = new_date.getUTCMonth();
18939 new_date.setUTCDate(day);
18941 return new_month != new_date.getUTCMonth();
18944 // Common date-resetting loop -- if date is beyond end of month, make it
18947 new_date.setUTCDate(--day);
18948 new_date.setUTCMonth(new_month);
18953 moveYear: function(date, dir)
18955 return this.moveMonth(date, dir*12);
18958 dateWithinRange: function(date)
18960 return date >= this.startDate && date <= this.endDate;
18966 this.picker().remove();
18969 validateValue : function(value)
18971 if(value.length < 1) {
18972 if(this.allowBlank){
18978 if(value.length < this.minLength){
18981 if(value.length > this.maxLength){
18985 var vt = Roo.form.VTypes;
18986 if(!vt[this.vtype](value, this)){
18990 if(typeof this.validator == "function"){
18991 var msg = this.validator(value);
18997 if(this.regex && !this.regex.test(value)){
19001 if(typeof(this.parseDate(value)) == 'undefined'){
19005 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19009 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19019 Roo.apply(Roo.bootstrap.DateField, {
19030 html: '<i class="fa fa-arrow-left"/>'
19040 html: '<i class="fa fa-arrow-right"/>'
19082 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19083 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19084 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19085 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19086 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19099 navFnc: 'FullYear',
19104 navFnc: 'FullYear',
19109 Roo.apply(Roo.bootstrap.DateField, {
19113 cls: 'datepicker dropdown-menu roo-dynamic',
19117 cls: 'datepicker-days',
19121 cls: 'table-condensed',
19123 Roo.bootstrap.DateField.head,
19127 Roo.bootstrap.DateField.footer
19134 cls: 'datepicker-months',
19138 cls: 'table-condensed',
19140 Roo.bootstrap.DateField.head,
19141 Roo.bootstrap.DateField.content,
19142 Roo.bootstrap.DateField.footer
19149 cls: 'datepicker-years',
19153 cls: 'table-condensed',
19155 Roo.bootstrap.DateField.head,
19156 Roo.bootstrap.DateField.content,
19157 Roo.bootstrap.DateField.footer
19176 * @class Roo.bootstrap.TimeField
19177 * @extends Roo.bootstrap.Input
19178 * Bootstrap DateField class
19182 * Create a new TimeField
19183 * @param {Object} config The config object
19186 Roo.bootstrap.TimeField = function(config){
19187 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19191 * Fires when this field show.
19192 * @param {Roo.bootstrap.DateField} thisthis
19193 * @param {Mixed} date The date value
19198 * Fires when this field hide.
19199 * @param {Roo.bootstrap.DateField} this
19200 * @param {Mixed} date The date value
19205 * Fires when select a date.
19206 * @param {Roo.bootstrap.DateField} this
19207 * @param {Mixed} date The date value
19213 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19216 * @cfg {String} format
19217 * The default time format string which can be overriden for localization support. The format must be
19218 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19222 onRender: function(ct, position)
19225 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19227 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19229 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19231 this.pop = this.picker().select('>.datepicker-time',true).first();
19232 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19234 this.picker().on('mousedown', this.onMousedown, this);
19235 this.picker().on('click', this.onClick, this);
19237 this.picker().addClass('datepicker-dropdown');
19242 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19243 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19244 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19245 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19246 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19247 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19251 fireKey: function(e){
19252 if (!this.picker().isVisible()){
19253 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19259 e.preventDefault();
19267 this.onTogglePeriod();
19270 this.onIncrementMinutes();
19273 this.onDecrementMinutes();
19282 onClick: function(e) {
19283 e.stopPropagation();
19284 e.preventDefault();
19287 picker : function()
19289 return this.el.select('.datepicker', true).first();
19292 fillTime: function()
19294 var time = this.pop.select('tbody', true).first();
19296 time.dom.innerHTML = '';
19311 cls: 'hours-up glyphicon glyphicon-chevron-up'
19331 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19352 cls: 'timepicker-hour',
19367 cls: 'timepicker-minute',
19382 cls: 'btn btn-primary period',
19404 cls: 'hours-down glyphicon glyphicon-chevron-down'
19424 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19442 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19449 var hours = this.time.getHours();
19450 var minutes = this.time.getMinutes();
19463 hours = hours - 12;
19467 hours = '0' + hours;
19471 minutes = '0' + minutes;
19474 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19475 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19476 this.pop.select('button', true).first().dom.innerHTML = period;
19482 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19484 var cls = ['bottom'];
19486 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19493 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19498 this.picker().addClass(cls.join('-'));
19502 Roo.each(cls, function(c){
19504 _this.picker().setTop(_this.inputEl().getHeight());
19508 _this.picker().setTop(0 - _this.picker().getHeight());
19513 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19517 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19524 onFocus : function()
19526 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19530 onBlur : function()
19532 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19538 this.picker().show();
19543 this.fireEvent('show', this, this.date);
19548 this.picker().hide();
19551 this.fireEvent('hide', this, this.date);
19554 setTime : function()
19557 this.setValue(this.time.format(this.format));
19559 this.fireEvent('select', this, this.date);
19564 onMousedown: function(e){
19565 e.stopPropagation();
19566 e.preventDefault();
19569 onIncrementHours: function()
19571 Roo.log('onIncrementHours');
19572 this.time = this.time.add(Date.HOUR, 1);
19577 onDecrementHours: function()
19579 Roo.log('onDecrementHours');
19580 this.time = this.time.add(Date.HOUR, -1);
19584 onIncrementMinutes: function()
19586 Roo.log('onIncrementMinutes');
19587 this.time = this.time.add(Date.MINUTE, 1);
19591 onDecrementMinutes: function()
19593 Roo.log('onDecrementMinutes');
19594 this.time = this.time.add(Date.MINUTE, -1);
19598 onTogglePeriod: function()
19600 Roo.log('onTogglePeriod');
19601 this.time = this.time.add(Date.HOUR, 12);
19608 Roo.apply(Roo.bootstrap.TimeField, {
19638 cls: 'btn btn-info ok',
19650 Roo.apply(Roo.bootstrap.TimeField, {
19654 cls: 'datepicker dropdown-menu',
19658 cls: 'datepicker-time',
19662 cls: 'table-condensed',
19664 Roo.bootstrap.TimeField.content,
19665 Roo.bootstrap.TimeField.footer
19684 * @class Roo.bootstrap.MonthField
19685 * @extends Roo.bootstrap.Input
19686 * Bootstrap MonthField class
19688 * @cfg {String} language default en
19691 * Create a new MonthField
19692 * @param {Object} config The config object
19695 Roo.bootstrap.MonthField = function(config){
19696 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19701 * Fires when this field show.
19702 * @param {Roo.bootstrap.MonthField} this
19703 * @param {Mixed} date The date value
19708 * Fires when this field hide.
19709 * @param {Roo.bootstrap.MonthField} this
19710 * @param {Mixed} date The date value
19715 * Fires when select a date.
19716 * @param {Roo.bootstrap.MonthField} this
19717 * @param {String} oldvalue The old value
19718 * @param {String} newvalue The new value
19724 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19726 onRender: function(ct, position)
19729 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19731 this.language = this.language || 'en';
19732 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19733 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19735 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19736 this.isInline = false;
19737 this.isInput = true;
19738 this.component = this.el.select('.add-on', true).first() || false;
19739 this.component = (this.component && this.component.length === 0) ? false : this.component;
19740 this.hasInput = this.component && this.inputEL().length;
19742 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19744 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19746 this.picker().on('mousedown', this.onMousedown, this);
19747 this.picker().on('click', this.onClick, this);
19749 this.picker().addClass('datepicker-dropdown');
19751 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19752 v.setStyle('width', '189px');
19759 if(this.isInline) {
19765 setValue: function(v, suppressEvent)
19767 var o = this.getValue();
19769 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19773 if(suppressEvent !== true){
19774 this.fireEvent('select', this, o, v);
19779 getValue: function()
19784 onClick: function(e)
19786 e.stopPropagation();
19787 e.preventDefault();
19789 var target = e.getTarget();
19791 if(target.nodeName.toLowerCase() === 'i'){
19792 target = Roo.get(target).dom.parentNode;
19795 var nodeName = target.nodeName;
19796 var className = target.className;
19797 var html = target.innerHTML;
19799 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19803 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19805 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19811 picker : function()
19813 return this.pickerEl;
19816 fillMonths: function()
19819 var months = this.picker().select('>.datepicker-months td', true).first();
19821 months.dom.innerHTML = '';
19827 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19830 months.createChild(month);
19839 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19840 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19843 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19844 e.removeClass('active');
19846 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19847 e.addClass('active');
19854 if(this.isInline) {
19858 this.picker().removeClass(['bottom', 'top']);
19860 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19862 * place to the top of element!
19866 this.picker().addClass('top');
19867 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19872 this.picker().addClass('bottom');
19874 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19877 onFocus : function()
19879 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19883 onBlur : function()
19885 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19887 var d = this.inputEl().getValue();
19896 this.picker().show();
19897 this.picker().select('>.datepicker-months', true).first().show();
19901 this.fireEvent('show', this, this.date);
19906 if(this.isInline) {
19909 this.picker().hide();
19910 this.fireEvent('hide', this, this.date);
19914 onMousedown: function(e)
19916 e.stopPropagation();
19917 e.preventDefault();
19922 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19926 fireKey: function(e)
19928 if (!this.picker().isVisible()){
19929 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19940 e.preventDefault();
19944 dir = e.keyCode == 37 ? -1 : 1;
19946 this.vIndex = this.vIndex + dir;
19948 if(this.vIndex < 0){
19952 if(this.vIndex > 11){
19956 if(isNaN(this.vIndex)){
19960 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19966 dir = e.keyCode == 38 ? -1 : 1;
19968 this.vIndex = this.vIndex + dir * 4;
19970 if(this.vIndex < 0){
19974 if(this.vIndex > 11){
19978 if(isNaN(this.vIndex)){
19982 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19987 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19988 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19992 e.preventDefault();
19995 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19996 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20012 this.picker().remove();
20017 Roo.apply(Roo.bootstrap.MonthField, {
20036 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20037 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20042 Roo.apply(Roo.bootstrap.MonthField, {
20046 cls: 'datepicker dropdown-menu roo-dynamic',
20050 cls: 'datepicker-months',
20054 cls: 'table-condensed',
20056 Roo.bootstrap.DateField.content
20076 * @class Roo.bootstrap.CheckBox
20077 * @extends Roo.bootstrap.Input
20078 * Bootstrap CheckBox class
20080 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20081 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20082 * @cfg {String} boxLabel The text that appears beside the checkbox
20083 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20084 * @cfg {Boolean} checked initnal the element
20085 * @cfg {Boolean} inline inline the element (default false)
20086 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20089 * Create a new CheckBox
20090 * @param {Object} config The config object
20093 Roo.bootstrap.CheckBox = function(config){
20094 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20099 * Fires when the element is checked or unchecked.
20100 * @param {Roo.bootstrap.CheckBox} this This input
20101 * @param {Boolean} checked The new checked value
20108 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20110 inputType: 'checkbox',
20118 getAutoCreate : function()
20120 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20126 cfg.cls = 'form-group ' + this.inputType; //input-group
20129 cfg.cls += ' ' + this.inputType + '-inline';
20135 type : this.inputType,
20136 value : this.inputValue,
20137 cls : 'roo-' + this.inputType, //'form-box',
20138 placeholder : this.placeholder || ''
20142 if(this.inputType != 'radio'){
20146 cls : 'roo-hidden-value',
20147 value : this.checked ? this.valueOff : this.inputValue
20152 if (this.weight) { // Validity check?
20153 cfg.cls += " " + this.inputType + "-" + this.weight;
20156 if (this.disabled) {
20157 input.disabled=true;
20161 input.checked = this.checked;
20168 input.name = this.name;
20170 if(this.inputType != 'radio'){
20171 hidden.name = this.name;
20172 input.name = '_hidden_' + this.name;
20177 input.cls += ' input-' + this.size;
20182 ['xs','sm','md','lg'].map(function(size){
20183 if (settings[size]) {
20184 cfg.cls += ' col-' + size + '-' + settings[size];
20188 var inputblock = input;
20190 if (this.before || this.after) {
20193 cls : 'input-group',
20198 inputblock.cn.push({
20200 cls : 'input-group-addon',
20205 inputblock.cn.push(input);
20207 if(this.inputType != 'radio'){
20208 inputblock.cn.push(hidden);
20212 inputblock.cn.push({
20214 cls : 'input-group-addon',
20221 if (align ==='left' && this.fieldLabel.length) {
20222 // Roo.log("left and has label");
20227 cls : 'control-label',
20228 html : this.fieldLabel
20239 if(this.labelWidth > 12){
20240 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20243 if(this.labelWidth < 13 && this.labelmd == 0){
20244 this.labelmd = this.labelWidth;
20247 if(this.labellg > 0){
20248 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20249 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20252 if(this.labelmd > 0){
20253 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20254 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20257 if(this.labelsm > 0){
20258 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20259 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20262 if(this.labelxs > 0){
20263 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20264 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20267 } else if ( this.fieldLabel.length) {
20268 // Roo.log(" label");
20272 tag: this.boxLabel ? 'span' : 'label',
20274 cls: 'control-label box-input-label',
20275 //cls : 'input-group-addon',
20276 html : this.fieldLabel
20286 // Roo.log(" no label && no align");
20287 cfg.cn = [ inputblock ] ;
20293 var boxLabelCfg = {
20295 //'for': id, // box label is handled by onclick - so no for...
20297 html: this.boxLabel
20301 boxLabelCfg.tooltip = this.tooltip;
20304 cfg.cn.push(boxLabelCfg);
20307 if(this.inputType != 'radio'){
20308 cfg.cn.push(hidden);
20316 * return the real input element.
20318 inputEl: function ()
20320 return this.el.select('input.roo-' + this.inputType,true).first();
20322 hiddenEl: function ()
20324 return this.el.select('input.roo-hidden-value',true).first();
20327 labelEl: function()
20329 return this.el.select('label.control-label',true).first();
20331 /* depricated... */
20335 return this.labelEl();
20338 boxLabelEl: function()
20340 return this.el.select('label.box-label',true).first();
20343 initEvents : function()
20345 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20347 this.inputEl().on('click', this.onClick, this);
20349 if (this.boxLabel) {
20350 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20353 this.startValue = this.getValue();
20356 Roo.bootstrap.CheckBox.register(this);
20360 onClick : function()
20362 this.setChecked(!this.checked);
20365 setChecked : function(state,suppressEvent)
20367 this.startValue = this.getValue();
20369 if(this.inputType == 'radio'){
20371 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20372 e.dom.checked = false;
20375 this.inputEl().dom.checked = true;
20377 this.inputEl().dom.value = this.inputValue;
20379 if(suppressEvent !== true){
20380 this.fireEvent('check', this, true);
20388 this.checked = state;
20390 this.inputEl().dom.checked = state;
20393 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20395 if(suppressEvent !== true){
20396 this.fireEvent('check', this, state);
20402 getValue : function()
20404 if(this.inputType == 'radio'){
20405 return this.getGroupValue();
20408 return this.hiddenEl().dom.value;
20412 getGroupValue : function()
20414 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20418 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20421 setValue : function(v,suppressEvent)
20423 if(this.inputType == 'radio'){
20424 this.setGroupValue(v, suppressEvent);
20428 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20433 setGroupValue : function(v, suppressEvent)
20435 this.startValue = this.getValue();
20437 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20438 e.dom.checked = false;
20440 if(e.dom.value == v){
20441 e.dom.checked = true;
20445 if(suppressEvent !== true){
20446 this.fireEvent('check', this, true);
20454 validate : function()
20458 (this.inputType == 'radio' && this.validateRadio()) ||
20459 (this.inputType == 'checkbox' && this.validateCheckbox())
20465 this.markInvalid();
20469 validateRadio : function()
20471 if(this.allowBlank){
20477 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20478 if(!e.dom.checked){
20490 validateCheckbox : function()
20493 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20494 //return (this.getValue() == this.inputValue) ? true : false;
20497 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20505 for(var i in group){
20510 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20517 * Mark this field as valid
20519 markValid : function()
20523 this.fireEvent('valid', this);
20525 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20528 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20535 if(this.inputType == 'radio'){
20536 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20537 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20538 e.findParent('.form-group', false, true).addClass(_this.validClass);
20545 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20546 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20550 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20556 for(var i in group){
20557 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20558 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20563 * Mark this field as invalid
20564 * @param {String} msg The validation message
20566 markInvalid : function(msg)
20568 if(this.allowBlank){
20574 this.fireEvent('invalid', this, msg);
20576 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20579 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20583 label.markInvalid();
20586 if(this.inputType == 'radio'){
20587 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20588 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20589 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20596 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20597 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20601 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20607 for(var i in group){
20608 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20609 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20614 clearInvalid : function()
20616 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20618 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20620 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20623 label.iconEl.removeClass(label.validClass);
20624 label.iconEl.removeClass(label.invalidClass);
20628 disable : function()
20630 if(this.inputType != 'radio'){
20631 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20638 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20639 _this.getActionEl().addClass(this.disabledClass);
20640 e.dom.disabled = true;
20644 this.disabled = true;
20645 this.fireEvent("disable", this);
20649 enable : function()
20651 if(this.inputType != 'radio'){
20652 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20659 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20660 _this.getActionEl().removeClass(this.disabledClass);
20661 e.dom.disabled = false;
20665 this.disabled = false;
20666 this.fireEvent("enable", this);
20672 Roo.apply(Roo.bootstrap.CheckBox, {
20677 * register a CheckBox Group
20678 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20680 register : function(checkbox)
20682 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20683 this.groups[checkbox.groupId] = {};
20686 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20690 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20694 * fetch a CheckBox Group based on the group ID
20695 * @param {string} the group ID
20696 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20698 get: function(groupId) {
20699 if (typeof(this.groups[groupId]) == 'undefined') {
20703 return this.groups[groupId] ;
20716 * @class Roo.bootstrap.Radio
20717 * @extends Roo.bootstrap.Component
20718 * Bootstrap Radio class
20719 * @cfg {String} boxLabel - the label associated
20720 * @cfg {String} value - the value of radio
20723 * Create a new Radio
20724 * @param {Object} config The config object
20726 Roo.bootstrap.Radio = function(config){
20727 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20731 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20737 getAutoCreate : function()
20741 cls : 'form-group radio',
20746 html : this.boxLabel
20754 initEvents : function()
20756 this.parent().register(this);
20758 this.el.on('click', this.onClick, this);
20762 onClick : function()
20764 this.setChecked(true);
20767 setChecked : function(state, suppressEvent)
20769 this.parent().setValue(this.value, suppressEvent);
20784 * @class Roo.bootstrap.SecurePass
20785 * @extends Roo.bootstrap.Input
20786 * Bootstrap SecurePass class
20790 * Create a new SecurePass
20791 * @param {Object} config The config object
20794 Roo.bootstrap.SecurePass = function (config) {
20795 // these go here, so the translation tool can replace them..
20797 PwdEmpty: "Please type a password, and then retype it to confirm.",
20798 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20799 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20800 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20801 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20802 FNInPwd: "Your password can't contain your first name. Please type a different password.",
20803 LNInPwd: "Your password can't contain your last name. Please type a different password.",
20804 TooWeak: "Your password is Too Weak."
20806 this.meterLabel = "Password strength:";
20807 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20808 this.meterClass = [
20809 "roo-password-meter-tooweak",
20810 "roo-password-meter-weak",
20811 "roo-password-meter-medium",
20812 "roo-password-meter-strong",
20813 "roo-password-meter-grey"
20818 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20821 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20823 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20825 * PwdEmpty: "Please type a password, and then retype it to confirm.",
20826 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20827 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20828 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20829 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20830 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
20831 * LNInPwd: "Your password can't contain your last name. Please type a different password."
20841 * @cfg {String/Object} Label for the strength meter (defaults to
20842 * 'Password strength:')
20847 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20848 * ['Weak', 'Medium', 'Strong'])
20851 pwdStrengths: false,
20864 initEvents: function ()
20866 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20868 if (this.el.is('input[type=password]') && Roo.isSafari) {
20869 this.el.on('keydown', this.SafariOnKeyDown, this);
20872 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20875 onRender: function (ct, position)
20877 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20878 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20879 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20881 this.trigger.createChild({
20886 cls: 'roo-password-meter-grey col-xs-12',
20889 //width: this.meterWidth + 'px'
20893 cls: 'roo-password-meter-text'
20899 if (this.hideTrigger) {
20900 this.trigger.setDisplayed(false);
20902 this.setSize(this.width || '', this.height || '');
20905 onDestroy: function ()
20907 if (this.trigger) {
20908 this.trigger.removeAllListeners();
20909 this.trigger.remove();
20912 this.wrap.remove();
20914 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20917 checkStrength: function ()
20919 var pwd = this.inputEl().getValue();
20920 if (pwd == this._lastPwd) {
20925 if (this.ClientSideStrongPassword(pwd)) {
20927 } else if (this.ClientSideMediumPassword(pwd)) {
20929 } else if (this.ClientSideWeakPassword(pwd)) {
20935 Roo.log('strength1: ' + strength);
20937 //var pm = this.trigger.child('div/div/div').dom;
20938 var pm = this.trigger.child('div/div');
20939 pm.removeClass(this.meterClass);
20940 pm.addClass(this.meterClass[strength]);
20943 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20945 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
20947 this._lastPwd = pwd;
20951 Roo.bootstrap.SecurePass.superclass.reset.call(this);
20953 this._lastPwd = '';
20955 var pm = this.trigger.child('div/div');
20956 pm.removeClass(this.meterClass);
20957 pm.addClass('roo-password-meter-grey');
20960 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
20963 this.inputEl().dom.type='password';
20966 validateValue: function (value)
20969 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20972 if (value.length == 0) {
20973 if (this.allowBlank) {
20974 this.clearInvalid();
20978 this.markInvalid(this.errors.PwdEmpty);
20979 this.errorMsg = this.errors.PwdEmpty;
20987 if ('[\x21-\x7e]*'.match(value)) {
20988 this.markInvalid(this.errors.PwdBadChar);
20989 this.errorMsg = this.errors.PwdBadChar;
20992 if (value.length < 6) {
20993 this.markInvalid(this.errors.PwdShort);
20994 this.errorMsg = this.errors.PwdShort;
20997 if (value.length > 16) {
20998 this.markInvalid(this.errors.PwdLong);
20999 this.errorMsg = this.errors.PwdLong;
21003 if (this.ClientSideStrongPassword(value)) {
21005 } else if (this.ClientSideMediumPassword(value)) {
21007 } else if (this.ClientSideWeakPassword(value)) {
21014 if (strength < 2) {
21015 //this.markInvalid(this.errors.TooWeak);
21016 this.errorMsg = this.errors.TooWeak;
21021 console.log('strength2: ' + strength);
21023 //var pm = this.trigger.child('div/div/div').dom;
21025 var pm = this.trigger.child('div/div');
21026 pm.removeClass(this.meterClass);
21027 pm.addClass(this.meterClass[strength]);
21029 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21031 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21033 this.errorMsg = '';
21037 CharacterSetChecks: function (type)
21040 this.fResult = false;
21043 isctype: function (character, type)
21046 case this.kCapitalLetter:
21047 if (character >= 'A' && character <= 'Z') {
21052 case this.kSmallLetter:
21053 if (character >= 'a' && character <= 'z') {
21059 if (character >= '0' && character <= '9') {
21064 case this.kPunctuation:
21065 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21076 IsLongEnough: function (pwd, size)
21078 return !(pwd == null || isNaN(size) || pwd.length < size);
21081 SpansEnoughCharacterSets: function (word, nb)
21083 if (!this.IsLongEnough(word, nb))
21088 var characterSetChecks = new Array(
21089 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21090 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21093 for (var index = 0; index < word.length; ++index) {
21094 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21095 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21096 characterSetChecks[nCharSet].fResult = true;
21103 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21104 if (characterSetChecks[nCharSet].fResult) {
21109 if (nCharSets < nb) {
21115 ClientSideStrongPassword: function (pwd)
21117 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21120 ClientSideMediumPassword: function (pwd)
21122 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21125 ClientSideWeakPassword: function (pwd)
21127 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21130 })//<script type="text/javascript">
21133 * Based Ext JS Library 1.1.1
21134 * Copyright(c) 2006-2007, Ext JS, LLC.
21140 * @class Roo.HtmlEditorCore
21141 * @extends Roo.Component
21142 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21144 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21147 Roo.HtmlEditorCore = function(config){
21150 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21155 * @event initialize
21156 * Fires when the editor is fully initialized (including the iframe)
21157 * @param {Roo.HtmlEditorCore} this
21162 * Fires when the editor is first receives the focus. Any insertion must wait
21163 * until after this event.
21164 * @param {Roo.HtmlEditorCore} this
21168 * @event beforesync
21169 * Fires before the textarea is updated with content from the editor iframe. Return false
21170 * to cancel the sync.
21171 * @param {Roo.HtmlEditorCore} this
21172 * @param {String} html
21176 * @event beforepush
21177 * Fires before the iframe editor is updated with content from the textarea. Return false
21178 * to cancel the push.
21179 * @param {Roo.HtmlEditorCore} this
21180 * @param {String} html
21185 * Fires when the textarea is updated with content from the editor iframe.
21186 * @param {Roo.HtmlEditorCore} this
21187 * @param {String} html
21192 * Fires when the iframe editor is updated with content from the textarea.
21193 * @param {Roo.HtmlEditorCore} this
21194 * @param {String} html
21199 * @event editorevent
21200 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21201 * @param {Roo.HtmlEditorCore} this
21207 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21209 // defaults : white / black...
21210 this.applyBlacklists();
21217 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21221 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21227 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21232 * @cfg {Number} height (in pixels)
21236 * @cfg {Number} width (in pixels)
21241 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21244 stylesheets: false,
21249 // private properties
21250 validationEvent : false,
21252 initialized : false,
21254 sourceEditMode : false,
21255 onFocus : Roo.emptyFn,
21257 hideMode:'offsets',
21261 // blacklist + whitelisted elements..
21268 * Protected method that will not generally be called directly. It
21269 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21270 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21272 getDocMarkup : function(){
21276 // inherit styels from page...??
21277 if (this.stylesheets === false) {
21279 Roo.get(document.head).select('style').each(function(node) {
21280 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21283 Roo.get(document.head).select('link').each(function(node) {
21284 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21287 } else if (!this.stylesheets.length) {
21289 st = '<style type="text/css">' +
21290 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21296 st += '<style type="text/css">' +
21297 'IMG { cursor: pointer } ' +
21301 return '<html><head>' + st +
21302 //<style type="text/css">' +
21303 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21305 ' </head><body class="roo-htmleditor-body"></body></html>';
21309 onRender : function(ct, position)
21312 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21313 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21316 this.el.dom.style.border = '0 none';
21317 this.el.dom.setAttribute('tabIndex', -1);
21318 this.el.addClass('x-hidden hide');
21322 if(Roo.isIE){ // fix IE 1px bogus margin
21323 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21327 this.frameId = Roo.id();
21331 var iframe = this.owner.wrap.createChild({
21333 cls: 'form-control', // bootstrap..
21335 name: this.frameId,
21336 frameBorder : 'no',
21337 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21342 this.iframe = iframe.dom;
21344 this.assignDocWin();
21346 this.doc.designMode = 'on';
21349 this.doc.write(this.getDocMarkup());
21353 var task = { // must defer to wait for browser to be ready
21355 //console.log("run task?" + this.doc.readyState);
21356 this.assignDocWin();
21357 if(this.doc.body || this.doc.readyState == 'complete'){
21359 this.doc.designMode="on";
21363 Roo.TaskMgr.stop(task);
21364 this.initEditor.defer(10, this);
21371 Roo.TaskMgr.start(task);
21376 onResize : function(w, h)
21378 Roo.log('resize: ' +w + ',' + h );
21379 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21383 if(typeof w == 'number'){
21385 this.iframe.style.width = w + 'px';
21387 if(typeof h == 'number'){
21389 this.iframe.style.height = h + 'px';
21391 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21398 * Toggles the editor between standard and source edit mode.
21399 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21401 toggleSourceEdit : function(sourceEditMode){
21403 this.sourceEditMode = sourceEditMode === true;
21405 if(this.sourceEditMode){
21407 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21410 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21411 //this.iframe.className = '';
21414 //this.setSize(this.owner.wrap.getSize());
21415 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21422 * Protected method that will not generally be called directly. If you need/want
21423 * custom HTML cleanup, this is the method you should override.
21424 * @param {String} html The HTML to be cleaned
21425 * return {String} The cleaned HTML
21427 cleanHtml : function(html){
21428 html = String(html);
21429 if(html.length > 5){
21430 if(Roo.isSafari){ // strip safari nonsense
21431 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21434 if(html == ' '){
21441 * HTML Editor -> Textarea
21442 * Protected method that will not generally be called directly. Syncs the contents
21443 * of the editor iframe with the textarea.
21445 syncValue : function(){
21446 if(this.initialized){
21447 var bd = (this.doc.body || this.doc.documentElement);
21448 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21449 var html = bd.innerHTML;
21451 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21452 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21454 html = '<div style="'+m[0]+'">' + html + '</div>';
21457 html = this.cleanHtml(html);
21458 // fix up the special chars.. normaly like back quotes in word...
21459 // however we do not want to do this with chinese..
21460 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21461 var cc = b.charCodeAt();
21463 (cc >= 0x4E00 && cc < 0xA000 ) ||
21464 (cc >= 0x3400 && cc < 0x4E00 ) ||
21465 (cc >= 0xf900 && cc < 0xfb00 )
21471 if(this.owner.fireEvent('beforesync', this, html) !== false){
21472 this.el.dom.value = html;
21473 this.owner.fireEvent('sync', this, html);
21479 * Protected method that will not generally be called directly. Pushes the value of the textarea
21480 * into the iframe editor.
21482 pushValue : function(){
21483 if(this.initialized){
21484 var v = this.el.dom.value.trim();
21486 // if(v.length < 1){
21490 if(this.owner.fireEvent('beforepush', this, v) !== false){
21491 var d = (this.doc.body || this.doc.documentElement);
21493 this.cleanUpPaste();
21494 this.el.dom.value = d.innerHTML;
21495 this.owner.fireEvent('push', this, v);
21501 deferFocus : function(){
21502 this.focus.defer(10, this);
21506 focus : function(){
21507 if(this.win && !this.sourceEditMode){
21514 assignDocWin: function()
21516 var iframe = this.iframe;
21519 this.doc = iframe.contentWindow.document;
21520 this.win = iframe.contentWindow;
21522 // if (!Roo.get(this.frameId)) {
21525 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21526 // this.win = Roo.get(this.frameId).dom.contentWindow;
21528 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21532 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21533 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21538 initEditor : function(){
21539 //console.log("INIT EDITOR");
21540 this.assignDocWin();
21544 this.doc.designMode="on";
21546 this.doc.write(this.getDocMarkup());
21549 var dbody = (this.doc.body || this.doc.documentElement);
21550 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21551 // this copies styles from the containing element into thsi one..
21552 // not sure why we need all of this..
21553 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21555 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21556 //ss['background-attachment'] = 'fixed'; // w3c
21557 dbody.bgProperties = 'fixed'; // ie
21558 //Roo.DomHelper.applyStyles(dbody, ss);
21559 Roo.EventManager.on(this.doc, {
21560 //'mousedown': this.onEditorEvent,
21561 'mouseup': this.onEditorEvent,
21562 'dblclick': this.onEditorEvent,
21563 'click': this.onEditorEvent,
21564 'keyup': this.onEditorEvent,
21569 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21571 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21572 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21574 this.initialized = true;
21576 this.owner.fireEvent('initialize', this);
21581 onDestroy : function(){
21587 //for (var i =0; i < this.toolbars.length;i++) {
21588 // // fixme - ask toolbars for heights?
21589 // this.toolbars[i].onDestroy();
21592 //this.wrap.dom.innerHTML = '';
21593 //this.wrap.remove();
21598 onFirstFocus : function(){
21600 this.assignDocWin();
21603 this.activated = true;
21606 if(Roo.isGecko){ // prevent silly gecko errors
21608 var s = this.win.getSelection();
21609 if(!s.focusNode || s.focusNode.nodeType != 3){
21610 var r = s.getRangeAt(0);
21611 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21616 this.execCmd('useCSS', true);
21617 this.execCmd('styleWithCSS', false);
21620 this.owner.fireEvent('activate', this);
21624 adjustFont: function(btn){
21625 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21626 //if(Roo.isSafari){ // safari
21629 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21630 if(Roo.isSafari){ // safari
21631 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21632 v = (v < 10) ? 10 : v;
21633 v = (v > 48) ? 48 : v;
21634 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21639 v = Math.max(1, v+adjust);
21641 this.execCmd('FontSize', v );
21644 onEditorEvent : function(e)
21646 this.owner.fireEvent('editorevent', this, e);
21647 // this.updateToolbar();
21648 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21651 insertTag : function(tg)
21653 // could be a bit smarter... -> wrap the current selected tRoo..
21654 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21656 range = this.createRange(this.getSelection());
21657 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21658 wrappingNode.appendChild(range.extractContents());
21659 range.insertNode(wrappingNode);
21666 this.execCmd("formatblock", tg);
21670 insertText : function(txt)
21674 var range = this.createRange();
21675 range.deleteContents();
21676 //alert(Sender.getAttribute('label'));
21678 range.insertNode(this.doc.createTextNode(txt));
21684 * Executes a Midas editor command on the editor document and performs necessary focus and
21685 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21686 * @param {String} cmd The Midas command
21687 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21689 relayCmd : function(cmd, value){
21691 this.execCmd(cmd, value);
21692 this.owner.fireEvent('editorevent', this);
21693 //this.updateToolbar();
21694 this.owner.deferFocus();
21698 * Executes a Midas editor command directly on the editor document.
21699 * For visual commands, you should use {@link #relayCmd} instead.
21700 * <b>This should only be called after the editor is initialized.</b>
21701 * @param {String} cmd The Midas command
21702 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21704 execCmd : function(cmd, value){
21705 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21712 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21714 * @param {String} text | dom node..
21716 insertAtCursor : function(text)
21719 if(!this.activated){
21725 var r = this.doc.selection.createRange();
21736 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21740 // from jquery ui (MIT licenced)
21742 var win = this.win;
21744 if (win.getSelection && win.getSelection().getRangeAt) {
21745 range = win.getSelection().getRangeAt(0);
21746 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21747 range.insertNode(node);
21748 } else if (win.document.selection && win.document.selection.createRange) {
21749 // no firefox support
21750 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21751 win.document.selection.createRange().pasteHTML(txt);
21753 // no firefox support
21754 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21755 this.execCmd('InsertHTML', txt);
21764 mozKeyPress : function(e){
21766 var c = e.getCharCode(), cmd;
21769 c = String.fromCharCode(c).toLowerCase();
21783 this.cleanUpPaste.defer(100, this);
21791 e.preventDefault();
21799 fixKeys : function(){ // load time branching for fastest keydown performance
21801 return function(e){
21802 var k = e.getKey(), r;
21805 r = this.doc.selection.createRange();
21808 r.pasteHTML('    ');
21815 r = this.doc.selection.createRange();
21817 var target = r.parentElement();
21818 if(!target || target.tagName.toLowerCase() != 'li'){
21820 r.pasteHTML('<br />');
21826 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21827 this.cleanUpPaste.defer(100, this);
21833 }else if(Roo.isOpera){
21834 return function(e){
21835 var k = e.getKey();
21839 this.execCmd('InsertHTML','    ');
21842 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21843 this.cleanUpPaste.defer(100, this);
21848 }else if(Roo.isSafari){
21849 return function(e){
21850 var k = e.getKey();
21854 this.execCmd('InsertText','\t');
21858 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21859 this.cleanUpPaste.defer(100, this);
21867 getAllAncestors: function()
21869 var p = this.getSelectedNode();
21872 a.push(p); // push blank onto stack..
21873 p = this.getParentElement();
21877 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21881 a.push(this.doc.body);
21885 lastSelNode : false,
21888 getSelection : function()
21890 this.assignDocWin();
21891 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21894 getSelectedNode: function()
21896 // this may only work on Gecko!!!
21898 // should we cache this!!!!
21903 var range = this.createRange(this.getSelection()).cloneRange();
21906 var parent = range.parentElement();
21908 var testRange = range.duplicate();
21909 testRange.moveToElementText(parent);
21910 if (testRange.inRange(range)) {
21913 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21916 parent = parent.parentElement;
21921 // is ancestor a text element.
21922 var ac = range.commonAncestorContainer;
21923 if (ac.nodeType == 3) {
21924 ac = ac.parentNode;
21927 var ar = ac.childNodes;
21930 var other_nodes = [];
21931 var has_other_nodes = false;
21932 for (var i=0;i<ar.length;i++) {
21933 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21936 // fullly contained node.
21938 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21943 // probably selected..
21944 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21945 other_nodes.push(ar[i]);
21949 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21954 has_other_nodes = true;
21956 if (!nodes.length && other_nodes.length) {
21957 nodes= other_nodes;
21959 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21965 createRange: function(sel)
21967 // this has strange effects when using with
21968 // top toolbar - not sure if it's a great idea.
21969 //this.editor.contentWindow.focus();
21970 if (typeof sel != "undefined") {
21972 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21974 return this.doc.createRange();
21977 return this.doc.createRange();
21980 getParentElement: function()
21983 this.assignDocWin();
21984 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21986 var range = this.createRange(sel);
21989 var p = range.commonAncestorContainer;
21990 while (p.nodeType == 3) { // text node
22001 * Range intersection.. the hard stuff...
22005 * [ -- selected range --- ]
22009 * if end is before start or hits it. fail.
22010 * if start is after end or hits it fail.
22012 * if either hits (but other is outside. - then it's not
22018 // @see http://www.thismuchiknow.co.uk/?p=64.
22019 rangeIntersectsNode : function(range, node)
22021 var nodeRange = node.ownerDocument.createRange();
22023 nodeRange.selectNode(node);
22025 nodeRange.selectNodeContents(node);
22028 var rangeStartRange = range.cloneRange();
22029 rangeStartRange.collapse(true);
22031 var rangeEndRange = range.cloneRange();
22032 rangeEndRange.collapse(false);
22034 var nodeStartRange = nodeRange.cloneRange();
22035 nodeStartRange.collapse(true);
22037 var nodeEndRange = nodeRange.cloneRange();
22038 nodeEndRange.collapse(false);
22040 return rangeStartRange.compareBoundaryPoints(
22041 Range.START_TO_START, nodeEndRange) == -1 &&
22042 rangeEndRange.compareBoundaryPoints(
22043 Range.START_TO_START, nodeStartRange) == 1;
22047 rangeCompareNode : function(range, node)
22049 var nodeRange = node.ownerDocument.createRange();
22051 nodeRange.selectNode(node);
22053 nodeRange.selectNodeContents(node);
22057 range.collapse(true);
22059 nodeRange.collapse(true);
22061 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22062 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22064 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22066 var nodeIsBefore = ss == 1;
22067 var nodeIsAfter = ee == -1;
22069 if (nodeIsBefore && nodeIsAfter) {
22072 if (!nodeIsBefore && nodeIsAfter) {
22073 return 1; //right trailed.
22076 if (nodeIsBefore && !nodeIsAfter) {
22077 return 2; // left trailed.
22083 // private? - in a new class?
22084 cleanUpPaste : function()
22086 // cleans up the whole document..
22087 Roo.log('cleanuppaste');
22089 this.cleanUpChildren(this.doc.body);
22090 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22091 if (clean != this.doc.body.innerHTML) {
22092 this.doc.body.innerHTML = clean;
22097 cleanWordChars : function(input) {// change the chars to hex code
22098 var he = Roo.HtmlEditorCore;
22100 var output = input;
22101 Roo.each(he.swapCodes, function(sw) {
22102 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22104 output = output.replace(swapper, sw[1]);
22111 cleanUpChildren : function (n)
22113 if (!n.childNodes.length) {
22116 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22117 this.cleanUpChild(n.childNodes[i]);
22124 cleanUpChild : function (node)
22127 //console.log(node);
22128 if (node.nodeName == "#text") {
22129 // clean up silly Windows -- stuff?
22132 if (node.nodeName == "#comment") {
22133 node.parentNode.removeChild(node);
22134 // clean up silly Windows -- stuff?
22137 var lcname = node.tagName.toLowerCase();
22138 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22139 // whitelist of tags..
22141 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22143 node.parentNode.removeChild(node);
22148 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22150 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22151 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22153 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22154 // remove_keep_children = true;
22157 if (remove_keep_children) {
22158 this.cleanUpChildren(node);
22159 // inserts everything just before this node...
22160 while (node.childNodes.length) {
22161 var cn = node.childNodes[0];
22162 node.removeChild(cn);
22163 node.parentNode.insertBefore(cn, node);
22165 node.parentNode.removeChild(node);
22169 if (!node.attributes || !node.attributes.length) {
22170 this.cleanUpChildren(node);
22174 function cleanAttr(n,v)
22177 if (v.match(/^\./) || v.match(/^\//)) {
22180 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22183 if (v.match(/^#/)) {
22186 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22187 node.removeAttribute(n);
22191 var cwhite = this.cwhite;
22192 var cblack = this.cblack;
22194 function cleanStyle(n,v)
22196 if (v.match(/expression/)) { //XSS?? should we even bother..
22197 node.removeAttribute(n);
22201 var parts = v.split(/;/);
22204 Roo.each(parts, function(p) {
22205 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22209 var l = p.split(':').shift().replace(/\s+/g,'');
22210 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22212 if ( cwhite.length && cblack.indexOf(l) > -1) {
22213 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22214 //node.removeAttribute(n);
22218 // only allow 'c whitelisted system attributes'
22219 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22220 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22221 //node.removeAttribute(n);
22231 if (clean.length) {
22232 node.setAttribute(n, clean.join(';'));
22234 node.removeAttribute(n);
22240 for (var i = node.attributes.length-1; i > -1 ; i--) {
22241 var a = node.attributes[i];
22244 if (a.name.toLowerCase().substr(0,2)=='on') {
22245 node.removeAttribute(a.name);
22248 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22249 node.removeAttribute(a.name);
22252 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22253 cleanAttr(a.name,a.value); // fixme..
22256 if (a.name == 'style') {
22257 cleanStyle(a.name,a.value);
22260 /// clean up MS crap..
22261 // tecnically this should be a list of valid class'es..
22264 if (a.name == 'class') {
22265 if (a.value.match(/^Mso/)) {
22266 node.className = '';
22269 if (a.value.match(/^body$/)) {
22270 node.className = '';
22281 this.cleanUpChildren(node);
22287 * Clean up MS wordisms...
22289 cleanWord : function(node)
22294 this.cleanWord(this.doc.body);
22297 if (node.nodeName == "#text") {
22298 // clean up silly Windows -- stuff?
22301 if (node.nodeName == "#comment") {
22302 node.parentNode.removeChild(node);
22303 // clean up silly Windows -- stuff?
22307 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22308 node.parentNode.removeChild(node);
22312 // remove - but keep children..
22313 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22314 while (node.childNodes.length) {
22315 var cn = node.childNodes[0];
22316 node.removeChild(cn);
22317 node.parentNode.insertBefore(cn, node);
22319 node.parentNode.removeChild(node);
22320 this.iterateChildren(node, this.cleanWord);
22324 if (node.className.length) {
22326 var cn = node.className.split(/\W+/);
22328 Roo.each(cn, function(cls) {
22329 if (cls.match(/Mso[a-zA-Z]+/)) {
22334 node.className = cna.length ? cna.join(' ') : '';
22336 node.removeAttribute("class");
22340 if (node.hasAttribute("lang")) {
22341 node.removeAttribute("lang");
22344 if (node.hasAttribute("style")) {
22346 var styles = node.getAttribute("style").split(";");
22348 Roo.each(styles, function(s) {
22349 if (!s.match(/:/)) {
22352 var kv = s.split(":");
22353 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22356 // what ever is left... we allow.
22359 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22360 if (!nstyle.length) {
22361 node.removeAttribute('style');
22364 this.iterateChildren(node, this.cleanWord);
22370 * iterateChildren of a Node, calling fn each time, using this as the scole..
22371 * @param {DomNode} node node to iterate children of.
22372 * @param {Function} fn method of this class to call on each item.
22374 iterateChildren : function(node, fn)
22376 if (!node.childNodes.length) {
22379 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22380 fn.call(this, node.childNodes[i])
22386 * cleanTableWidths.
22388 * Quite often pasting from word etc.. results in tables with column and widths.
22389 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22392 cleanTableWidths : function(node)
22397 this.cleanTableWidths(this.doc.body);
22402 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22405 Roo.log(node.tagName);
22406 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22407 this.iterateChildren(node, this.cleanTableWidths);
22410 if (node.hasAttribute('width')) {
22411 node.removeAttribute('width');
22415 if (node.hasAttribute("style")) {
22418 var styles = node.getAttribute("style").split(";");
22420 Roo.each(styles, function(s) {
22421 if (!s.match(/:/)) {
22424 var kv = s.split(":");
22425 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22428 // what ever is left... we allow.
22431 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22432 if (!nstyle.length) {
22433 node.removeAttribute('style');
22437 this.iterateChildren(node, this.cleanTableWidths);
22445 domToHTML : function(currentElement, depth, nopadtext) {
22447 depth = depth || 0;
22448 nopadtext = nopadtext || false;
22450 if (!currentElement) {
22451 return this.domToHTML(this.doc.body);
22454 //Roo.log(currentElement);
22456 var allText = false;
22457 var nodeName = currentElement.nodeName;
22458 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22460 if (nodeName == '#text') {
22462 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22467 if (nodeName != 'BODY') {
22470 // Prints the node tagName, such as <A>, <IMG>, etc
22473 for(i = 0; i < currentElement.attributes.length;i++) {
22475 var aname = currentElement.attributes.item(i).name;
22476 if (!currentElement.attributes.item(i).value.length) {
22479 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22482 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22491 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22494 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22499 // Traverse the tree
22501 var currentElementChild = currentElement.childNodes.item(i);
22502 var allText = true;
22503 var innerHTML = '';
22505 while (currentElementChild) {
22506 // Formatting code (indent the tree so it looks nice on the screen)
22507 var nopad = nopadtext;
22508 if (lastnode == 'SPAN') {
22512 if (currentElementChild.nodeName == '#text') {
22513 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22514 toadd = nopadtext ? toadd : toadd.trim();
22515 if (!nopad && toadd.length > 80) {
22516 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22518 innerHTML += toadd;
22521 currentElementChild = currentElement.childNodes.item(i);
22527 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22529 // Recursively traverse the tree structure of the child node
22530 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22531 lastnode = currentElementChild.nodeName;
22533 currentElementChild=currentElement.childNodes.item(i);
22539 // The remaining code is mostly for formatting the tree
22540 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22545 ret+= "</"+tagName+">";
22551 applyBlacklists : function()
22553 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22554 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22558 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22559 if (b.indexOf(tag) > -1) {
22562 this.white.push(tag);
22566 Roo.each(w, function(tag) {
22567 if (b.indexOf(tag) > -1) {
22570 if (this.white.indexOf(tag) > -1) {
22573 this.white.push(tag);
22578 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22579 if (w.indexOf(tag) > -1) {
22582 this.black.push(tag);
22586 Roo.each(b, function(tag) {
22587 if (w.indexOf(tag) > -1) {
22590 if (this.black.indexOf(tag) > -1) {
22593 this.black.push(tag);
22598 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22599 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22603 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22604 if (b.indexOf(tag) > -1) {
22607 this.cwhite.push(tag);
22611 Roo.each(w, function(tag) {
22612 if (b.indexOf(tag) > -1) {
22615 if (this.cwhite.indexOf(tag) > -1) {
22618 this.cwhite.push(tag);
22623 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22624 if (w.indexOf(tag) > -1) {
22627 this.cblack.push(tag);
22631 Roo.each(b, function(tag) {
22632 if (w.indexOf(tag) > -1) {
22635 if (this.cblack.indexOf(tag) > -1) {
22638 this.cblack.push(tag);
22643 setStylesheets : function(stylesheets)
22645 if(typeof(stylesheets) == 'string'){
22646 Roo.get(this.iframe.contentDocument.head).createChild({
22648 rel : 'stylesheet',
22657 Roo.each(stylesheets, function(s) {
22662 Roo.get(_this.iframe.contentDocument.head).createChild({
22664 rel : 'stylesheet',
22673 removeStylesheets : function()
22677 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22682 // hide stuff that is not compatible
22696 * @event specialkey
22700 * @cfg {String} fieldClass @hide
22703 * @cfg {String} focusClass @hide
22706 * @cfg {String} autoCreate @hide
22709 * @cfg {String} inputType @hide
22712 * @cfg {String} invalidClass @hide
22715 * @cfg {String} invalidText @hide
22718 * @cfg {String} msgFx @hide
22721 * @cfg {String} validateOnBlur @hide
22725 Roo.HtmlEditorCore.white = [
22726 'area', 'br', 'img', 'input', 'hr', 'wbr',
22728 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22729 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22730 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22731 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22732 'table', 'ul', 'xmp',
22734 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22737 'dir', 'menu', 'ol', 'ul', 'dl',
22743 Roo.HtmlEditorCore.black = [
22744 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22746 'base', 'basefont', 'bgsound', 'blink', 'body',
22747 'frame', 'frameset', 'head', 'html', 'ilayer',
22748 'iframe', 'layer', 'link', 'meta', 'object',
22749 'script', 'style' ,'title', 'xml' // clean later..
22751 Roo.HtmlEditorCore.clean = [
22752 'script', 'style', 'title', 'xml'
22754 Roo.HtmlEditorCore.remove = [
22759 Roo.HtmlEditorCore.ablack = [
22763 Roo.HtmlEditorCore.aclean = [
22764 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22768 Roo.HtmlEditorCore.pwhite= [
22769 'http', 'https', 'mailto'
22772 // white listed style attributes.
22773 Roo.HtmlEditorCore.cwhite= [
22774 // 'text-align', /// default is to allow most things..
22780 // black listed style attributes.
22781 Roo.HtmlEditorCore.cblack= [
22782 // 'font-size' -- this can be set by the project
22786 Roo.HtmlEditorCore.swapCodes =[
22805 * @class Roo.bootstrap.HtmlEditor
22806 * @extends Roo.bootstrap.TextArea
22807 * Bootstrap HtmlEditor class
22810 * Create a new HtmlEditor
22811 * @param {Object} config The config object
22814 Roo.bootstrap.HtmlEditor = function(config){
22815 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22816 if (!this.toolbars) {
22817 this.toolbars = [];
22820 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22823 * @event initialize
22824 * Fires when the editor is fully initialized (including the iframe)
22825 * @param {HtmlEditor} this
22830 * Fires when the editor is first receives the focus. Any insertion must wait
22831 * until after this event.
22832 * @param {HtmlEditor} this
22836 * @event beforesync
22837 * Fires before the textarea is updated with content from the editor iframe. Return false
22838 * to cancel the sync.
22839 * @param {HtmlEditor} this
22840 * @param {String} html
22844 * @event beforepush
22845 * Fires before the iframe editor is updated with content from the textarea. Return false
22846 * to cancel the push.
22847 * @param {HtmlEditor} this
22848 * @param {String} html
22853 * Fires when the textarea is updated with content from the editor iframe.
22854 * @param {HtmlEditor} this
22855 * @param {String} html
22860 * Fires when the iframe editor is updated with content from the textarea.
22861 * @param {HtmlEditor} this
22862 * @param {String} html
22866 * @event editmodechange
22867 * Fires when the editor switches edit modes
22868 * @param {HtmlEditor} this
22869 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22871 editmodechange: true,
22873 * @event editorevent
22874 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22875 * @param {HtmlEditor} this
22879 * @event firstfocus
22880 * Fires when on first focus - needed by toolbars..
22881 * @param {HtmlEditor} this
22886 * Auto save the htmlEditor value as a file into Events
22887 * @param {HtmlEditor} this
22891 * @event savedpreview
22892 * preview the saved version of htmlEditor
22893 * @param {HtmlEditor} this
22900 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22904 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22909 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22914 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22919 * @cfg {Number} height (in pixels)
22923 * @cfg {Number} width (in pixels)
22928 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22931 stylesheets: false,
22936 // private properties
22937 validationEvent : false,
22939 initialized : false,
22942 onFocus : Roo.emptyFn,
22944 hideMode:'offsets',
22946 tbContainer : false,
22948 toolbarContainer :function() {
22949 return this.wrap.select('.x-html-editor-tb',true).first();
22953 * Protected method that will not generally be called directly. It
22954 * is called when the editor creates its toolbar. Override this method if you need to
22955 * add custom toolbar buttons.
22956 * @param {HtmlEditor} editor
22958 createToolbar : function(){
22959 Roo.log('renewing');
22960 Roo.log("create toolbars");
22962 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22963 this.toolbars[0].render(this.toolbarContainer());
22967 // if (!editor.toolbars || !editor.toolbars.length) {
22968 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22971 // for (var i =0 ; i < editor.toolbars.length;i++) {
22972 // editor.toolbars[i] = Roo.factory(
22973 // typeof(editor.toolbars[i]) == 'string' ?
22974 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22975 // Roo.bootstrap.HtmlEditor);
22976 // editor.toolbars[i].init(editor);
22982 onRender : function(ct, position)
22984 // Roo.log("Call onRender: " + this.xtype);
22986 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22988 this.wrap = this.inputEl().wrap({
22989 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22992 this.editorcore.onRender(ct, position);
22994 if (this.resizable) {
22995 this.resizeEl = new Roo.Resizable(this.wrap, {
22999 minHeight : this.height,
23000 height: this.height,
23001 handles : this.resizable,
23004 resize : function(r, w, h) {
23005 _t.onResize(w,h); // -something
23011 this.createToolbar(this);
23014 if(!this.width && this.resizable){
23015 this.setSize(this.wrap.getSize());
23017 if (this.resizeEl) {
23018 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23019 // should trigger onReize..
23025 onResize : function(w, h)
23027 Roo.log('resize: ' +w + ',' + h );
23028 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23032 if(this.inputEl() ){
23033 if(typeof w == 'number'){
23034 var aw = w - this.wrap.getFrameWidth('lr');
23035 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23038 if(typeof h == 'number'){
23039 var tbh = -11; // fixme it needs to tool bar size!
23040 for (var i =0; i < this.toolbars.length;i++) {
23041 // fixme - ask toolbars for heights?
23042 tbh += this.toolbars[i].el.getHeight();
23043 //if (this.toolbars[i].footer) {
23044 // tbh += this.toolbars[i].footer.el.getHeight();
23052 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23053 ah -= 5; // knock a few pixes off for look..
23054 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23058 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23059 this.editorcore.onResize(ew,eh);
23064 * Toggles the editor between standard and source edit mode.
23065 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23067 toggleSourceEdit : function(sourceEditMode)
23069 this.editorcore.toggleSourceEdit(sourceEditMode);
23071 if(this.editorcore.sourceEditMode){
23072 Roo.log('editor - showing textarea');
23075 // Roo.log(this.syncValue());
23077 this.inputEl().removeClass(['hide', 'x-hidden']);
23078 this.inputEl().dom.removeAttribute('tabIndex');
23079 this.inputEl().focus();
23081 Roo.log('editor - hiding textarea');
23083 // Roo.log(this.pushValue());
23086 this.inputEl().addClass(['hide', 'x-hidden']);
23087 this.inputEl().dom.setAttribute('tabIndex', -1);
23088 //this.deferFocus();
23091 if(this.resizable){
23092 this.setSize(this.wrap.getSize());
23095 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23098 // private (for BoxComponent)
23099 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23101 // private (for BoxComponent)
23102 getResizeEl : function(){
23106 // private (for BoxComponent)
23107 getPositionEl : function(){
23112 initEvents : function(){
23113 this.originalValue = this.getValue();
23117 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23120 // markInvalid : Roo.emptyFn,
23122 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23125 // clearInvalid : Roo.emptyFn,
23127 setValue : function(v){
23128 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23129 this.editorcore.pushValue();
23134 deferFocus : function(){
23135 this.focus.defer(10, this);
23139 focus : function(){
23140 this.editorcore.focus();
23146 onDestroy : function(){
23152 for (var i =0; i < this.toolbars.length;i++) {
23153 // fixme - ask toolbars for heights?
23154 this.toolbars[i].onDestroy();
23157 this.wrap.dom.innerHTML = '';
23158 this.wrap.remove();
23163 onFirstFocus : function(){
23164 //Roo.log("onFirstFocus");
23165 this.editorcore.onFirstFocus();
23166 for (var i =0; i < this.toolbars.length;i++) {
23167 this.toolbars[i].onFirstFocus();
23173 syncValue : function()
23175 this.editorcore.syncValue();
23178 pushValue : function()
23180 this.editorcore.pushValue();
23184 // hide stuff that is not compatible
23198 * @event specialkey
23202 * @cfg {String} fieldClass @hide
23205 * @cfg {String} focusClass @hide
23208 * @cfg {String} autoCreate @hide
23211 * @cfg {String} inputType @hide
23214 * @cfg {String} invalidClass @hide
23217 * @cfg {String} invalidText @hide
23220 * @cfg {String} msgFx @hide
23223 * @cfg {String} validateOnBlur @hide
23232 Roo.namespace('Roo.bootstrap.htmleditor');
23234 * @class Roo.bootstrap.HtmlEditorToolbar1
23239 new Roo.bootstrap.HtmlEditor({
23242 new Roo.bootstrap.HtmlEditorToolbar1({
23243 disable : { fonts: 1 , format: 1, ..., ... , ...],
23249 * @cfg {Object} disable List of elements to disable..
23250 * @cfg {Array} btns List of additional buttons.
23254 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23257 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23260 Roo.apply(this, config);
23262 // default disabled, based on 'good practice'..
23263 this.disable = this.disable || {};
23264 Roo.applyIf(this.disable, {
23267 specialElements : true
23269 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23271 this.editor = config.editor;
23272 this.editorcore = config.editor.editorcore;
23274 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23276 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23277 // dont call parent... till later.
23279 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23284 editorcore : false,
23289 "h1","h2","h3","h4","h5","h6",
23291 "abbr", "acronym", "address", "cite", "samp", "var",
23295 onRender : function(ct, position)
23297 // Roo.log("Call onRender: " + this.xtype);
23299 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23301 this.el.dom.style.marginBottom = '0';
23303 var editorcore = this.editorcore;
23304 var editor= this.editor;
23307 var btn = function(id,cmd , toggle, handler, html){
23309 var event = toggle ? 'toggle' : 'click';
23314 xns: Roo.bootstrap,
23317 enableToggle:toggle !== false,
23319 pressed : toggle ? false : null,
23322 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23323 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23329 // var cb_box = function...
23334 xns: Roo.bootstrap,
23335 glyphicon : 'font',
23339 xns: Roo.bootstrap,
23343 Roo.each(this.formats, function(f) {
23344 style.menu.items.push({
23346 xns: Roo.bootstrap,
23347 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23352 editorcore.insertTag(this.tagname);
23359 children.push(style);
23361 btn('bold',false,true);
23362 btn('italic',false,true);
23363 btn('align-left', 'justifyleft',true);
23364 btn('align-center', 'justifycenter',true);
23365 btn('align-right' , 'justifyright',true);
23366 btn('link', false, false, function(btn) {
23367 //Roo.log("create link?");
23368 var url = prompt(this.createLinkText, this.defaultLinkValue);
23369 if(url && url != 'http:/'+'/'){
23370 this.editorcore.relayCmd('createlink', url);
23373 btn('list','insertunorderedlist',true);
23374 btn('pencil', false,true, function(btn){
23376 this.toggleSourceEdit(btn.pressed);
23379 if (this.editor.btns.length > 0) {
23380 for (var i = 0; i<this.editor.btns.length; i++) {
23381 children.push(this.editor.btns[i]);
23389 xns: Roo.bootstrap,
23394 xns: Roo.bootstrap,
23399 cog.menu.items.push({
23401 xns: Roo.bootstrap,
23402 html : Clean styles,
23407 editorcore.insertTag(this.tagname);
23416 this.xtype = 'NavSimplebar';
23418 for(var i=0;i< children.length;i++) {
23420 this.buttons.add(this.addxtypeChild(children[i]));
23424 editor.on('editorevent', this.updateToolbar, this);
23426 onBtnClick : function(id)
23428 this.editorcore.relayCmd(id);
23429 this.editorcore.focus();
23433 * Protected method that will not generally be called directly. It triggers
23434 * a toolbar update by reading the markup state of the current selection in the editor.
23436 updateToolbar: function(){
23438 if(!this.editorcore.activated){
23439 this.editor.onFirstFocus(); // is this neeed?
23443 var btns = this.buttons;
23444 var doc = this.editorcore.doc;
23445 btns.get('bold').setActive(doc.queryCommandState('bold'));
23446 btns.get('italic').setActive(doc.queryCommandState('italic'));
23447 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23449 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23450 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23451 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23453 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23454 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23457 var ans = this.editorcore.getAllAncestors();
23458 if (this.formatCombo) {
23461 var store = this.formatCombo.store;
23462 this.formatCombo.setValue("");
23463 for (var i =0; i < ans.length;i++) {
23464 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23466 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23474 // hides menus... - so this cant be on a menu...
23475 Roo.bootstrap.MenuMgr.hideAll();
23477 Roo.bootstrap.MenuMgr.hideAll();
23478 //this.editorsyncValue();
23480 onFirstFocus: function() {
23481 this.buttons.each(function(item){
23485 toggleSourceEdit : function(sourceEditMode){
23488 if(sourceEditMode){
23489 Roo.log("disabling buttons");
23490 this.buttons.each( function(item){
23491 if(item.cmd != 'pencil'){
23497 Roo.log("enabling buttons");
23498 if(this.editorcore.initialized){
23499 this.buttons.each( function(item){
23505 Roo.log("calling toggole on editor");
23506 // tell the editor that it's been pressed..
23507 this.editor.toggleSourceEdit(sourceEditMode);
23517 * @class Roo.bootstrap.Table.AbstractSelectionModel
23518 * @extends Roo.util.Observable
23519 * Abstract base class for grid SelectionModels. It provides the interface that should be
23520 * implemented by descendant classes. This class should not be directly instantiated.
23523 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23524 this.locked = false;
23525 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23529 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23530 /** @ignore Called by the grid automatically. Do not call directly. */
23531 init : function(grid){
23537 * Locks the selections.
23540 this.locked = true;
23544 * Unlocks the selections.
23546 unlock : function(){
23547 this.locked = false;
23551 * Returns true if the selections are locked.
23552 * @return {Boolean}
23554 isLocked : function(){
23555 return this.locked;
23559 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23560 * @class Roo.bootstrap.Table.RowSelectionModel
23561 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23562 * It supports multiple selections and keyboard selection/navigation.
23564 * @param {Object} config
23567 Roo.bootstrap.Table.RowSelectionModel = function(config){
23568 Roo.apply(this, config);
23569 this.selections = new Roo.util.MixedCollection(false, function(o){
23574 this.lastActive = false;
23578 * @event selectionchange
23579 * Fires when the selection changes
23580 * @param {SelectionModel} this
23582 "selectionchange" : true,
23584 * @event afterselectionchange
23585 * Fires after the selection changes (eg. by key press or clicking)
23586 * @param {SelectionModel} this
23588 "afterselectionchange" : true,
23590 * @event beforerowselect
23591 * Fires when a row is selected being selected, return false to cancel.
23592 * @param {SelectionModel} this
23593 * @param {Number} rowIndex The selected index
23594 * @param {Boolean} keepExisting False if other selections will be cleared
23596 "beforerowselect" : true,
23599 * Fires when a row is selected.
23600 * @param {SelectionModel} this
23601 * @param {Number} rowIndex The selected index
23602 * @param {Roo.data.Record} r The record
23604 "rowselect" : true,
23606 * @event rowdeselect
23607 * Fires when a row is deselected.
23608 * @param {SelectionModel} this
23609 * @param {Number} rowIndex The selected index
23611 "rowdeselect" : true
23613 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23614 this.locked = false;
23617 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23619 * @cfg {Boolean} singleSelect
23620 * True to allow selection of only one row at a time (defaults to false)
23622 singleSelect : false,
23625 initEvents : function()
23628 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23629 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23630 //}else{ // allow click to work like normal
23631 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23633 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23634 this.grid.on("rowclick", this.handleMouseDown, this);
23636 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23637 "up" : function(e){
23639 this.selectPrevious(e.shiftKey);
23640 }else if(this.last !== false && this.lastActive !== false){
23641 var last = this.last;
23642 this.selectRange(this.last, this.lastActive-1);
23643 this.grid.getView().focusRow(this.lastActive);
23644 if(last !== false){
23648 this.selectFirstRow();
23650 this.fireEvent("afterselectionchange", this);
23652 "down" : function(e){
23654 this.selectNext(e.shiftKey);
23655 }else if(this.last !== false && this.lastActive !== false){
23656 var last = this.last;
23657 this.selectRange(this.last, this.lastActive+1);
23658 this.grid.getView().focusRow(this.lastActive);
23659 if(last !== false){
23663 this.selectFirstRow();
23665 this.fireEvent("afterselectionchange", this);
23669 this.grid.store.on('load', function(){
23670 this.selections.clear();
23673 var view = this.grid.view;
23674 view.on("refresh", this.onRefresh, this);
23675 view.on("rowupdated", this.onRowUpdated, this);
23676 view.on("rowremoved", this.onRemove, this);
23681 onRefresh : function()
23683 var ds = this.grid.store, i, v = this.grid.view;
23684 var s = this.selections;
23685 s.each(function(r){
23686 if((i = ds.indexOfId(r.id)) != -1){
23695 onRemove : function(v, index, r){
23696 this.selections.remove(r);
23700 onRowUpdated : function(v, index, r){
23701 if(this.isSelected(r)){
23702 v.onRowSelect(index);
23708 * @param {Array} records The records to select
23709 * @param {Boolean} keepExisting (optional) True to keep existing selections
23711 selectRecords : function(records, keepExisting)
23714 this.clearSelections();
23716 var ds = this.grid.store;
23717 for(var i = 0, len = records.length; i < len; i++){
23718 this.selectRow(ds.indexOf(records[i]), true);
23723 * Gets the number of selected rows.
23726 getCount : function(){
23727 return this.selections.length;
23731 * Selects the first row in the grid.
23733 selectFirstRow : function(){
23738 * Select the last row.
23739 * @param {Boolean} keepExisting (optional) True to keep existing selections
23741 selectLastRow : function(keepExisting){
23742 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23743 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23747 * Selects the row immediately following the last selected row.
23748 * @param {Boolean} keepExisting (optional) True to keep existing selections
23750 selectNext : function(keepExisting)
23752 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23753 this.selectRow(this.last+1, keepExisting);
23754 this.grid.getView().focusRow(this.last);
23759 * Selects the row that precedes the last selected row.
23760 * @param {Boolean} keepExisting (optional) True to keep existing selections
23762 selectPrevious : function(keepExisting){
23764 this.selectRow(this.last-1, keepExisting);
23765 this.grid.getView().focusRow(this.last);
23770 * Returns the selected records
23771 * @return {Array} Array of selected records
23773 getSelections : function(){
23774 return [].concat(this.selections.items);
23778 * Returns the first selected record.
23781 getSelected : function(){
23782 return this.selections.itemAt(0);
23787 * Clears all selections.
23789 clearSelections : function(fast)
23795 var ds = this.grid.store;
23796 var s = this.selections;
23797 s.each(function(r){
23798 this.deselectRow(ds.indexOfId(r.id));
23802 this.selections.clear();
23809 * Selects all rows.
23811 selectAll : function(){
23815 this.selections.clear();
23816 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23817 this.selectRow(i, true);
23822 * Returns True if there is a selection.
23823 * @return {Boolean}
23825 hasSelection : function(){
23826 return this.selections.length > 0;
23830 * Returns True if the specified row is selected.
23831 * @param {Number/Record} record The record or index of the record to check
23832 * @return {Boolean}
23834 isSelected : function(index){
23835 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23836 return (r && this.selections.key(r.id) ? true : false);
23840 * Returns True if the specified record id is selected.
23841 * @param {String} id The id of record to check
23842 * @return {Boolean}
23844 isIdSelected : function(id){
23845 return (this.selections.key(id) ? true : false);
23850 handleMouseDBClick : function(e, t){
23854 handleMouseDown : function(e, t)
23856 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23857 if(this.isLocked() || rowIndex < 0 ){
23860 if(e.shiftKey && this.last !== false){
23861 var last = this.last;
23862 this.selectRange(last, rowIndex, e.ctrlKey);
23863 this.last = last; // reset the last
23867 var isSelected = this.isSelected(rowIndex);
23868 //Roo.log("select row:" + rowIndex);
23870 this.deselectRow(rowIndex);
23872 this.selectRow(rowIndex, true);
23876 if(e.button !== 0 && isSelected){
23877 alert('rowIndex 2: ' + rowIndex);
23878 view.focusRow(rowIndex);
23879 }else if(e.ctrlKey && isSelected){
23880 this.deselectRow(rowIndex);
23881 }else if(!isSelected){
23882 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23883 view.focusRow(rowIndex);
23887 this.fireEvent("afterselectionchange", this);
23890 handleDragableRowClick : function(grid, rowIndex, e)
23892 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23893 this.selectRow(rowIndex, false);
23894 grid.view.focusRow(rowIndex);
23895 this.fireEvent("afterselectionchange", this);
23900 * Selects multiple rows.
23901 * @param {Array} rows Array of the indexes of the row to select
23902 * @param {Boolean} keepExisting (optional) True to keep existing selections
23904 selectRows : function(rows, keepExisting){
23906 this.clearSelections();
23908 for(var i = 0, len = rows.length; i < len; i++){
23909 this.selectRow(rows[i], true);
23914 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23915 * @param {Number} startRow The index of the first row in the range
23916 * @param {Number} endRow The index of the last row in the range
23917 * @param {Boolean} keepExisting (optional) True to retain existing selections
23919 selectRange : function(startRow, endRow, keepExisting){
23924 this.clearSelections();
23926 if(startRow <= endRow){
23927 for(var i = startRow; i <= endRow; i++){
23928 this.selectRow(i, true);
23931 for(var i = startRow; i >= endRow; i--){
23932 this.selectRow(i, true);
23938 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23939 * @param {Number} startRow The index of the first row in the range
23940 * @param {Number} endRow The index of the last row in the range
23942 deselectRange : function(startRow, endRow, preventViewNotify){
23946 for(var i = startRow; i <= endRow; i++){
23947 this.deselectRow(i, preventViewNotify);
23953 * @param {Number} row The index of the row to select
23954 * @param {Boolean} keepExisting (optional) True to keep existing selections
23956 selectRow : function(index, keepExisting, preventViewNotify)
23958 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23961 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23962 if(!keepExisting || this.singleSelect){
23963 this.clearSelections();
23966 var r = this.grid.store.getAt(index);
23967 //console.log('selectRow - record id :' + r.id);
23969 this.selections.add(r);
23970 this.last = this.lastActive = index;
23971 if(!preventViewNotify){
23972 var proxy = new Roo.Element(
23973 this.grid.getRowDom(index)
23975 proxy.addClass('bg-info info');
23977 this.fireEvent("rowselect", this, index, r);
23978 this.fireEvent("selectionchange", this);
23984 * @param {Number} row The index of the row to deselect
23986 deselectRow : function(index, preventViewNotify)
23991 if(this.last == index){
23994 if(this.lastActive == index){
23995 this.lastActive = false;
23998 var r = this.grid.store.getAt(index);
24003 this.selections.remove(r);
24004 //.console.log('deselectRow - record id :' + r.id);
24005 if(!preventViewNotify){
24007 var proxy = new Roo.Element(
24008 this.grid.getRowDom(index)
24010 proxy.removeClass('bg-info info');
24012 this.fireEvent("rowdeselect", this, index);
24013 this.fireEvent("selectionchange", this);
24017 restoreLast : function(){
24019 this.last = this._last;
24024 acceptsNav : function(row, col, cm){
24025 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24029 onEditorKey : function(field, e){
24030 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24035 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24037 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24039 }else if(k == e.ENTER && !e.ctrlKey){
24043 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24045 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24047 }else if(k == e.ESC){
24051 g.startEditing(newCell[0], newCell[1]);
24057 * Ext JS Library 1.1.1
24058 * Copyright(c) 2006-2007, Ext JS, LLC.
24060 * Originally Released Under LGPL - original licence link has changed is not relivant.
24063 * <script type="text/javascript">
24067 * @class Roo.bootstrap.PagingToolbar
24068 * @extends Roo.bootstrap.NavSimplebar
24069 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24071 * Create a new PagingToolbar
24072 * @param {Object} config The config object
24073 * @param {Roo.data.Store} store
24075 Roo.bootstrap.PagingToolbar = function(config)
24077 // old args format still supported... - xtype is prefered..
24078 // created from xtype...
24080 this.ds = config.dataSource;
24082 if (config.store && !this.ds) {
24083 this.store= Roo.factory(config.store, Roo.data);
24084 this.ds = this.store;
24085 this.ds.xmodule = this.xmodule || false;
24088 this.toolbarItems = [];
24089 if (config.items) {
24090 this.toolbarItems = config.items;
24093 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24098 this.bind(this.ds);
24101 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24105 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24107 * @cfg {Roo.data.Store} dataSource
24108 * The underlying data store providing the paged data
24111 * @cfg {String/HTMLElement/Element} container
24112 * container The id or element that will contain the toolbar
24115 * @cfg {Boolean} displayInfo
24116 * True to display the displayMsg (defaults to false)
24119 * @cfg {Number} pageSize
24120 * The number of records to display per page (defaults to 20)
24124 * @cfg {String} displayMsg
24125 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24127 displayMsg : 'Displaying {0} - {1} of {2}',
24129 * @cfg {String} emptyMsg
24130 * The message to display when no records are found (defaults to "No data to display")
24132 emptyMsg : 'No data to display',
24134 * Customizable piece of the default paging text (defaults to "Page")
24137 beforePageText : "Page",
24139 * Customizable piece of the default paging text (defaults to "of %0")
24142 afterPageText : "of {0}",
24144 * Customizable piece of the default paging text (defaults to "First Page")
24147 firstText : "First Page",
24149 * Customizable piece of the default paging text (defaults to "Previous Page")
24152 prevText : "Previous Page",
24154 * Customizable piece of the default paging text (defaults to "Next Page")
24157 nextText : "Next Page",
24159 * Customizable piece of the default paging text (defaults to "Last Page")
24162 lastText : "Last Page",
24164 * Customizable piece of the default paging text (defaults to "Refresh")
24167 refreshText : "Refresh",
24171 onRender : function(ct, position)
24173 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24174 this.navgroup.parentId = this.id;
24175 this.navgroup.onRender(this.el, null);
24176 // add the buttons to the navgroup
24178 if(this.displayInfo){
24179 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24180 this.displayEl = this.el.select('.x-paging-info', true).first();
24181 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24182 // this.displayEl = navel.el.select('span',true).first();
24188 Roo.each(_this.buttons, function(e){ // this might need to use render????
24189 Roo.factory(e).onRender(_this.el, null);
24193 Roo.each(_this.toolbarItems, function(e) {
24194 _this.navgroup.addItem(e);
24198 this.first = this.navgroup.addItem({
24199 tooltip: this.firstText,
24201 icon : 'fa fa-backward',
24203 preventDefault: true,
24204 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24207 this.prev = this.navgroup.addItem({
24208 tooltip: this.prevText,
24210 icon : 'fa fa-step-backward',
24212 preventDefault: true,
24213 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24215 //this.addSeparator();
24218 var field = this.navgroup.addItem( {
24220 cls : 'x-paging-position',
24222 html : this.beforePageText +
24223 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24224 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24227 this.field = field.el.select('input', true).first();
24228 this.field.on("keydown", this.onPagingKeydown, this);
24229 this.field.on("focus", function(){this.dom.select();});
24232 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24233 //this.field.setHeight(18);
24234 //this.addSeparator();
24235 this.next = this.navgroup.addItem({
24236 tooltip: this.nextText,
24238 html : ' <i class="fa fa-step-forward">',
24240 preventDefault: true,
24241 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24243 this.last = this.navgroup.addItem({
24244 tooltip: this.lastText,
24245 icon : 'fa fa-forward',
24248 preventDefault: true,
24249 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24251 //this.addSeparator();
24252 this.loading = this.navgroup.addItem({
24253 tooltip: this.refreshText,
24254 icon: 'fa fa-refresh',
24255 preventDefault: true,
24256 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24262 updateInfo : function(){
24263 if(this.displayEl){
24264 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24265 var msg = count == 0 ?
24269 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24271 this.displayEl.update(msg);
24276 onLoad : function(ds, r, o)
24278 this.cursor = o.params ? o.params.start : 0;
24279 var d = this.getPageData(),
24284 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24285 this.field.dom.value = ap;
24286 this.first.setDisabled(ap == 1);
24287 this.prev.setDisabled(ap == 1);
24288 this.next.setDisabled(ap == ps);
24289 this.last.setDisabled(ap == ps);
24290 this.loading.enable();
24295 getPageData : function(){
24296 var total = this.ds.getTotalCount();
24299 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24300 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24305 onLoadError : function(){
24306 this.loading.enable();
24310 onPagingKeydown : function(e){
24311 var k = e.getKey();
24312 var d = this.getPageData();
24314 var v = this.field.dom.value, pageNum;
24315 if(!v || isNaN(pageNum = parseInt(v, 10))){
24316 this.field.dom.value = d.activePage;
24319 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24320 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24323 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))
24325 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24326 this.field.dom.value = pageNum;
24327 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24330 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24332 var v = this.field.dom.value, pageNum;
24333 var increment = (e.shiftKey) ? 10 : 1;
24334 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24337 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24338 this.field.dom.value = d.activePage;
24341 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24343 this.field.dom.value = parseInt(v, 10) + increment;
24344 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24345 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24352 beforeLoad : function(){
24354 this.loading.disable();
24359 onClick : function(which){
24368 ds.load({params:{start: 0, limit: this.pageSize}});
24371 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24374 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24377 var total = ds.getTotalCount();
24378 var extra = total % this.pageSize;
24379 var lastStart = extra ? (total - extra) : total-this.pageSize;
24380 ds.load({params:{start: lastStart, limit: this.pageSize}});
24383 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24389 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24390 * @param {Roo.data.Store} store The data store to unbind
24392 unbind : function(ds){
24393 ds.un("beforeload", this.beforeLoad, this);
24394 ds.un("load", this.onLoad, this);
24395 ds.un("loadexception", this.onLoadError, this);
24396 ds.un("remove", this.updateInfo, this);
24397 ds.un("add", this.updateInfo, this);
24398 this.ds = undefined;
24402 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24403 * @param {Roo.data.Store} store The data store to bind
24405 bind : function(ds){
24406 ds.on("beforeload", this.beforeLoad, this);
24407 ds.on("load", this.onLoad, this);
24408 ds.on("loadexception", this.onLoadError, this);
24409 ds.on("remove", this.updateInfo, this);
24410 ds.on("add", this.updateInfo, this);
24421 * @class Roo.bootstrap.MessageBar
24422 * @extends Roo.bootstrap.Component
24423 * Bootstrap MessageBar class
24424 * @cfg {String} html contents of the MessageBar
24425 * @cfg {String} weight (info | success | warning | danger) default info
24426 * @cfg {String} beforeClass insert the bar before the given class
24427 * @cfg {Boolean} closable (true | false) default false
24428 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24431 * Create a new Element
24432 * @param {Object} config The config object
24435 Roo.bootstrap.MessageBar = function(config){
24436 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24439 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24445 beforeClass: 'bootstrap-sticky-wrap',
24447 getAutoCreate : function(){
24451 cls: 'alert alert-dismissable alert-' + this.weight,
24456 html: this.html || ''
24462 cfg.cls += ' alert-messages-fixed';
24476 onRender : function(ct, position)
24478 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24481 var cfg = Roo.apply({}, this.getAutoCreate());
24485 cfg.cls += ' ' + this.cls;
24488 cfg.style = this.style;
24490 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24492 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24495 this.el.select('>button.close').on('click', this.hide, this);
24501 if (!this.rendered) {
24507 this.fireEvent('show', this);
24513 if (!this.rendered) {
24519 this.fireEvent('hide', this);
24522 update : function()
24524 // var e = this.el.dom.firstChild;
24526 // if(this.closable){
24527 // e = e.nextSibling;
24530 // e.data = this.html || '';
24532 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24548 * @class Roo.bootstrap.Graph
24549 * @extends Roo.bootstrap.Component
24550 * Bootstrap Graph class
24554 @cfg {String} graphtype bar | vbar | pie
24555 @cfg {number} g_x coodinator | centre x (pie)
24556 @cfg {number} g_y coodinator | centre y (pie)
24557 @cfg {number} g_r radius (pie)
24558 @cfg {number} g_height height of the chart (respected by all elements in the set)
24559 @cfg {number} g_width width of the chart (respected by all elements in the set)
24560 @cfg {Object} title The title of the chart
24563 -opts (object) options for the chart
24565 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24566 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24568 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.
24569 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24571 o stretch (boolean)
24573 -opts (object) options for the pie
24576 o startAngle (number)
24577 o endAngle (number)
24581 * Create a new Input
24582 * @param {Object} config The config object
24585 Roo.bootstrap.Graph = function(config){
24586 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24592 * The img click event for the img.
24593 * @param {Roo.EventObject} e
24599 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24610 //g_colors: this.colors,
24617 getAutoCreate : function(){
24628 onRender : function(ct,position){
24631 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24633 if (typeof(Raphael) == 'undefined') {
24634 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24638 this.raphael = Raphael(this.el.dom);
24640 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24641 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24642 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24643 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24645 r.text(160, 10, "Single Series Chart").attr(txtattr);
24646 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24647 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24648 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24650 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24651 r.barchart(330, 10, 300, 220, data1);
24652 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24653 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24656 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24657 // r.barchart(30, 30, 560, 250, xdata, {
24658 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24659 // axis : "0 0 1 1",
24660 // axisxlabels : xdata
24661 // //yvalues : cols,
24664 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24666 // this.load(null,xdata,{
24667 // axis : "0 0 1 1",
24668 // axisxlabels : xdata
24673 load : function(graphtype,xdata,opts)
24675 this.raphael.clear();
24677 graphtype = this.graphtype;
24682 var r = this.raphael,
24683 fin = function () {
24684 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24686 fout = function () {
24687 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24689 pfin = function() {
24690 this.sector.stop();
24691 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24694 this.label[0].stop();
24695 this.label[0].attr({ r: 7.5 });
24696 this.label[1].attr({ "font-weight": 800 });
24699 pfout = function() {
24700 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24703 this.label[0].animate({ r: 5 }, 500, "bounce");
24704 this.label[1].attr({ "font-weight": 400 });
24710 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24713 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24716 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
24717 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24719 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24726 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24731 setTitle: function(o)
24736 initEvents: function() {
24739 this.el.on('click', this.onClick, this);
24743 onClick : function(e)
24745 Roo.log('img onclick');
24746 this.fireEvent('click', this, e);
24758 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24761 * @class Roo.bootstrap.dash.NumberBox
24762 * @extends Roo.bootstrap.Component
24763 * Bootstrap NumberBox class
24764 * @cfg {String} headline Box headline
24765 * @cfg {String} content Box content
24766 * @cfg {String} icon Box icon
24767 * @cfg {String} footer Footer text
24768 * @cfg {String} fhref Footer href
24771 * Create a new NumberBox
24772 * @param {Object} config The config object
24776 Roo.bootstrap.dash.NumberBox = function(config){
24777 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24781 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24790 getAutoCreate : function(){
24794 cls : 'small-box ',
24802 cls : 'roo-headline',
24803 html : this.headline
24807 cls : 'roo-content',
24808 html : this.content
24822 cls : 'ion ' + this.icon
24831 cls : 'small-box-footer',
24832 href : this.fhref || '#',
24836 cfg.cn.push(footer);
24843 onRender : function(ct,position){
24844 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24851 setHeadline: function (value)
24853 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24856 setFooter: function (value, href)
24858 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24861 this.el.select('a.small-box-footer',true).first().attr('href', href);
24866 setContent: function (value)
24868 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24871 initEvents: function()
24885 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24888 * @class Roo.bootstrap.dash.TabBox
24889 * @extends Roo.bootstrap.Component
24890 * Bootstrap TabBox class
24891 * @cfg {String} title Title of the TabBox
24892 * @cfg {String} icon Icon of the TabBox
24893 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24894 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24897 * Create a new TabBox
24898 * @param {Object} config The config object
24902 Roo.bootstrap.dash.TabBox = function(config){
24903 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24908 * When a pane is added
24909 * @param {Roo.bootstrap.dash.TabPane} pane
24913 * @event activatepane
24914 * When a pane is activated
24915 * @param {Roo.bootstrap.dash.TabPane} pane
24917 "activatepane" : true
24925 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24930 tabScrollable : false,
24932 getChildContainer : function()
24934 return this.el.select('.tab-content', true).first();
24937 getAutoCreate : function(){
24941 cls: 'pull-left header',
24949 cls: 'fa ' + this.icon
24955 cls: 'nav nav-tabs pull-right',
24961 if(this.tabScrollable){
24968 cls: 'nav nav-tabs pull-right',
24979 cls: 'nav-tabs-custom',
24984 cls: 'tab-content no-padding',
24992 initEvents : function()
24994 //Roo.log('add add pane handler');
24995 this.on('addpane', this.onAddPane, this);
24998 * Updates the box title
24999 * @param {String} html to set the title to.
25001 setTitle : function(value)
25003 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25005 onAddPane : function(pane)
25007 this.panes.push(pane);
25008 //Roo.log('addpane');
25010 // tabs are rendere left to right..
25011 if(!this.showtabs){
25015 var ctr = this.el.select('.nav-tabs', true).first();
25018 var existing = ctr.select('.nav-tab',true);
25019 var qty = existing.getCount();;
25022 var tab = ctr.createChild({
25024 cls : 'nav-tab' + (qty ? '' : ' active'),
25032 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25035 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25037 pane.el.addClass('active');
25042 onTabClick : function(ev,un,ob,pane)
25044 //Roo.log('tab - prev default');
25045 ev.preventDefault();
25048 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25049 pane.tab.addClass('active');
25050 //Roo.log(pane.title);
25051 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25052 // technically we should have a deactivate event.. but maybe add later.
25053 // and it should not de-activate the selected tab...
25054 this.fireEvent('activatepane', pane);
25055 pane.el.addClass('active');
25056 pane.fireEvent('activate');
25061 getActivePane : function()
25064 Roo.each(this.panes, function(p) {
25065 if(p.el.hasClass('active')){
25086 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25088 * @class Roo.bootstrap.TabPane
25089 * @extends Roo.bootstrap.Component
25090 * Bootstrap TabPane class
25091 * @cfg {Boolean} active (false | true) Default false
25092 * @cfg {String} title title of panel
25096 * Create a new TabPane
25097 * @param {Object} config The config object
25100 Roo.bootstrap.dash.TabPane = function(config){
25101 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25107 * When a pane is activated
25108 * @param {Roo.bootstrap.dash.TabPane} pane
25115 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25120 // the tabBox that this is attached to.
25123 getAutoCreate : function()
25131 cfg.cls += ' active';
25136 initEvents : function()
25138 //Roo.log('trigger add pane handler');
25139 this.parent().fireEvent('addpane', this)
25143 * Updates the tab title
25144 * @param {String} html to set the title to.
25146 setTitle: function(str)
25152 this.tab.select('a', true).first().dom.innerHTML = str;
25169 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25172 * @class Roo.bootstrap.menu.Menu
25173 * @extends Roo.bootstrap.Component
25174 * Bootstrap Menu class - container for Menu
25175 * @cfg {String} html Text of the menu
25176 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25177 * @cfg {String} icon Font awesome icon
25178 * @cfg {String} pos Menu align to (top | bottom) default bottom
25182 * Create a new Menu
25183 * @param {Object} config The config object
25187 Roo.bootstrap.menu.Menu = function(config){
25188 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25192 * @event beforeshow
25193 * Fires before this menu is displayed
25194 * @param {Roo.bootstrap.menu.Menu} this
25198 * @event beforehide
25199 * Fires before this menu is hidden
25200 * @param {Roo.bootstrap.menu.Menu} this
25205 * Fires after this menu is displayed
25206 * @param {Roo.bootstrap.menu.Menu} this
25211 * Fires after this menu is hidden
25212 * @param {Roo.bootstrap.menu.Menu} this
25217 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25218 * @param {Roo.bootstrap.menu.Menu} this
25219 * @param {Roo.EventObject} e
25226 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25230 weight : 'default',
25235 getChildContainer : function() {
25236 if(this.isSubMenu){
25240 return this.el.select('ul.dropdown-menu', true).first();
25243 getAutoCreate : function()
25248 cls : 'roo-menu-text',
25256 cls : 'fa ' + this.icon
25267 cls : 'dropdown-button btn btn-' + this.weight,
25272 cls : 'dropdown-toggle btn btn-' + this.weight,
25282 cls : 'dropdown-menu'
25288 if(this.pos == 'top'){
25289 cfg.cls += ' dropup';
25292 if(this.isSubMenu){
25295 cls : 'dropdown-menu'
25302 onRender : function(ct, position)
25304 this.isSubMenu = ct.hasClass('dropdown-submenu');
25306 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25309 initEvents : function()
25311 if(this.isSubMenu){
25315 this.hidden = true;
25317 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25318 this.triggerEl.on('click', this.onTriggerPress, this);
25320 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25321 this.buttonEl.on('click', this.onClick, this);
25327 if(this.isSubMenu){
25331 return this.el.select('ul.dropdown-menu', true).first();
25334 onClick : function(e)
25336 this.fireEvent("click", this, e);
25339 onTriggerPress : function(e)
25341 if (this.isVisible()) {
25348 isVisible : function(){
25349 return !this.hidden;
25354 this.fireEvent("beforeshow", this);
25356 this.hidden = false;
25357 this.el.addClass('open');
25359 Roo.get(document).on("mouseup", this.onMouseUp, this);
25361 this.fireEvent("show", this);
25368 this.fireEvent("beforehide", this);
25370 this.hidden = true;
25371 this.el.removeClass('open');
25373 Roo.get(document).un("mouseup", this.onMouseUp);
25375 this.fireEvent("hide", this);
25378 onMouseUp : function()
25392 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25395 * @class Roo.bootstrap.menu.Item
25396 * @extends Roo.bootstrap.Component
25397 * Bootstrap MenuItem class
25398 * @cfg {Boolean} submenu (true | false) default false
25399 * @cfg {String} html text of the item
25400 * @cfg {String} href the link
25401 * @cfg {Boolean} disable (true | false) default false
25402 * @cfg {Boolean} preventDefault (true | false) default true
25403 * @cfg {String} icon Font awesome icon
25404 * @cfg {String} pos Submenu align to (left | right) default right
25408 * Create a new Item
25409 * @param {Object} config The config object
25413 Roo.bootstrap.menu.Item = function(config){
25414 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25418 * Fires when the mouse is hovering over this menu
25419 * @param {Roo.bootstrap.menu.Item} this
25420 * @param {Roo.EventObject} e
25425 * Fires when the mouse exits this menu
25426 * @param {Roo.bootstrap.menu.Item} this
25427 * @param {Roo.EventObject} e
25433 * The raw click event for the entire grid.
25434 * @param {Roo.EventObject} e
25440 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25445 preventDefault: true,
25450 getAutoCreate : function()
25455 cls : 'roo-menu-item-text',
25463 cls : 'fa ' + this.icon
25472 href : this.href || '#',
25479 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25483 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25485 if(this.pos == 'left'){
25486 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25493 initEvents : function()
25495 this.el.on('mouseover', this.onMouseOver, this);
25496 this.el.on('mouseout', this.onMouseOut, this);
25498 this.el.select('a', true).first().on('click', this.onClick, this);
25502 onClick : function(e)
25504 if(this.preventDefault){
25505 e.preventDefault();
25508 this.fireEvent("click", this, e);
25511 onMouseOver : function(e)
25513 if(this.submenu && this.pos == 'left'){
25514 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25517 this.fireEvent("mouseover", this, e);
25520 onMouseOut : function(e)
25522 this.fireEvent("mouseout", this, e);
25534 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25537 * @class Roo.bootstrap.menu.Separator
25538 * @extends Roo.bootstrap.Component
25539 * Bootstrap Separator class
25542 * Create a new Separator
25543 * @param {Object} config The config object
25547 Roo.bootstrap.menu.Separator = function(config){
25548 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25551 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25553 getAutoCreate : function(){
25574 * @class Roo.bootstrap.Tooltip
25575 * Bootstrap Tooltip class
25576 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25577 * to determine which dom element triggers the tooltip.
25579 * It needs to add support for additional attributes like tooltip-position
25582 * Create a new Toolti
25583 * @param {Object} config The config object
25586 Roo.bootstrap.Tooltip = function(config){
25587 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25589 this.alignment = Roo.bootstrap.Tooltip.alignment;
25591 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25592 this.alignment = config.alignment;
25597 Roo.apply(Roo.bootstrap.Tooltip, {
25599 * @function init initialize tooltip monitoring.
25603 currentTip : false,
25604 currentRegion : false,
25610 Roo.get(document).on('mouseover', this.enter ,this);
25611 Roo.get(document).on('mouseout', this.leave, this);
25614 this.currentTip = new Roo.bootstrap.Tooltip();
25617 enter : function(ev)
25619 var dom = ev.getTarget();
25621 //Roo.log(['enter',dom]);
25622 var el = Roo.fly(dom);
25623 if (this.currentEl) {
25625 //Roo.log(this.currentEl);
25626 //Roo.log(this.currentEl.contains(dom));
25627 if (this.currentEl == el) {
25630 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25636 if (this.currentTip.el) {
25637 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25641 if(!el || el.dom == document){
25647 // you can not look for children, as if el is the body.. then everythign is the child..
25648 if (!el.attr('tooltip')) { //
25649 if (!el.select("[tooltip]").elements.length) {
25652 // is the mouse over this child...?
25653 bindEl = el.select("[tooltip]").first();
25654 var xy = ev.getXY();
25655 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25656 //Roo.log("not in region.");
25659 //Roo.log("child element over..");
25662 this.currentEl = bindEl;
25663 this.currentTip.bind(bindEl);
25664 this.currentRegion = Roo.lib.Region.getRegion(dom);
25665 this.currentTip.enter();
25668 leave : function(ev)
25670 var dom = ev.getTarget();
25671 //Roo.log(['leave',dom]);
25672 if (!this.currentEl) {
25677 if (dom != this.currentEl.dom) {
25680 var xy = ev.getXY();
25681 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
25684 // only activate leave if mouse cursor is outside... bounding box..
25689 if (this.currentTip) {
25690 this.currentTip.leave();
25692 //Roo.log('clear currentEl');
25693 this.currentEl = false;
25698 'left' : ['r-l', [-2,0], 'right'],
25699 'right' : ['l-r', [2,0], 'left'],
25700 'bottom' : ['t-b', [0,2], 'top'],
25701 'top' : [ 'b-t', [0,-2], 'bottom']
25707 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
25712 delay : null, // can be { show : 300 , hide: 500}
25716 hoverState : null, //???
25718 placement : 'bottom',
25722 getAutoCreate : function(){
25729 cls : 'tooltip-arrow'
25732 cls : 'tooltip-inner'
25739 bind : function(el)
25745 enter : function () {
25747 if (this.timeout != null) {
25748 clearTimeout(this.timeout);
25751 this.hoverState = 'in';
25752 //Roo.log("enter - show");
25753 if (!this.delay || !this.delay.show) {
25758 this.timeout = setTimeout(function () {
25759 if (_t.hoverState == 'in') {
25762 }, this.delay.show);
25766 clearTimeout(this.timeout);
25768 this.hoverState = 'out';
25769 if (!this.delay || !this.delay.hide) {
25775 this.timeout = setTimeout(function () {
25776 //Roo.log("leave - timeout");
25778 if (_t.hoverState == 'out') {
25780 Roo.bootstrap.Tooltip.currentEl = false;
25785 show : function (msg)
25788 this.render(document.body);
25791 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25793 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25795 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25797 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25799 var placement = typeof this.placement == 'function' ?
25800 this.placement.call(this, this.el, on_el) :
25803 var autoToken = /\s?auto?\s?/i;
25804 var autoPlace = autoToken.test(placement);
25806 placement = placement.replace(autoToken, '') || 'top';
25810 //this.el.setXY([0,0]);
25812 //this.el.dom.style.display='block';
25814 //this.el.appendTo(on_el);
25816 var p = this.getPosition();
25817 var box = this.el.getBox();
25823 var align = this.alignment[placement];
25825 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25827 if(placement == 'top' || placement == 'bottom'){
25829 placement = 'right';
25832 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25833 placement = 'left';
25836 var scroll = Roo.select('body', true).first().getScroll();
25838 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25844 this.el.alignTo(this.bindEl, align[0],align[1]);
25845 //var arrow = this.el.select('.arrow',true).first();
25846 //arrow.set(align[2],
25848 this.el.addClass(placement);
25850 this.el.addClass('in fade');
25852 this.hoverState = null;
25854 if (this.el.hasClass('fade')) {
25865 //this.el.setXY([0,0]);
25866 this.el.removeClass('in');
25882 * @class Roo.bootstrap.LocationPicker
25883 * @extends Roo.bootstrap.Component
25884 * Bootstrap LocationPicker class
25885 * @cfg {Number} latitude Position when init default 0
25886 * @cfg {Number} longitude Position when init default 0
25887 * @cfg {Number} zoom default 15
25888 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25889 * @cfg {Boolean} mapTypeControl default false
25890 * @cfg {Boolean} disableDoubleClickZoom default false
25891 * @cfg {Boolean} scrollwheel default true
25892 * @cfg {Boolean} streetViewControl default false
25893 * @cfg {Number} radius default 0
25894 * @cfg {String} locationName
25895 * @cfg {Boolean} draggable default true
25896 * @cfg {Boolean} enableAutocomplete default false
25897 * @cfg {Boolean} enableReverseGeocode default true
25898 * @cfg {String} markerTitle
25901 * Create a new LocationPicker
25902 * @param {Object} config The config object
25906 Roo.bootstrap.LocationPicker = function(config){
25908 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25913 * Fires when the picker initialized.
25914 * @param {Roo.bootstrap.LocationPicker} this
25915 * @param {Google Location} location
25919 * @event positionchanged
25920 * Fires when the picker position changed.
25921 * @param {Roo.bootstrap.LocationPicker} this
25922 * @param {Google Location} location
25924 positionchanged : true,
25927 * Fires when the map resize.
25928 * @param {Roo.bootstrap.LocationPicker} this
25933 * Fires when the map show.
25934 * @param {Roo.bootstrap.LocationPicker} this
25939 * Fires when the map hide.
25940 * @param {Roo.bootstrap.LocationPicker} this
25945 * Fires when click the map.
25946 * @param {Roo.bootstrap.LocationPicker} this
25947 * @param {Map event} e
25951 * @event mapRightClick
25952 * Fires when right click the map.
25953 * @param {Roo.bootstrap.LocationPicker} this
25954 * @param {Map event} e
25956 mapRightClick : true,
25958 * @event markerClick
25959 * Fires when click the marker.
25960 * @param {Roo.bootstrap.LocationPicker} this
25961 * @param {Map event} e
25963 markerClick : true,
25965 * @event markerRightClick
25966 * Fires when right click the marker.
25967 * @param {Roo.bootstrap.LocationPicker} this
25968 * @param {Map event} e
25970 markerRightClick : true,
25972 * @event OverlayViewDraw
25973 * Fires when OverlayView Draw
25974 * @param {Roo.bootstrap.LocationPicker} this
25976 OverlayViewDraw : true,
25978 * @event OverlayViewOnAdd
25979 * Fires when OverlayView Draw
25980 * @param {Roo.bootstrap.LocationPicker} this
25982 OverlayViewOnAdd : true,
25984 * @event OverlayViewOnRemove
25985 * Fires when OverlayView Draw
25986 * @param {Roo.bootstrap.LocationPicker} this
25988 OverlayViewOnRemove : true,
25990 * @event OverlayViewShow
25991 * Fires when OverlayView Draw
25992 * @param {Roo.bootstrap.LocationPicker} this
25993 * @param {Pixel} cpx
25995 OverlayViewShow : true,
25997 * @event OverlayViewHide
25998 * Fires when OverlayView Draw
25999 * @param {Roo.bootstrap.LocationPicker} this
26001 OverlayViewHide : true,
26003 * @event loadexception
26004 * Fires when load google lib failed.
26005 * @param {Roo.bootstrap.LocationPicker} this
26007 loadexception : true
26012 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26014 gMapContext: false,
26020 mapTypeControl: false,
26021 disableDoubleClickZoom: false,
26023 streetViewControl: false,
26027 enableAutocomplete: false,
26028 enableReverseGeocode: true,
26031 getAutoCreate: function()
26036 cls: 'roo-location-picker'
26042 initEvents: function(ct, position)
26044 if(!this.el.getWidth() || this.isApplied()){
26048 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26053 initial: function()
26055 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26056 this.fireEvent('loadexception', this);
26060 if(!this.mapTypeId){
26061 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26064 this.gMapContext = this.GMapContext();
26066 this.initOverlayView();
26068 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26072 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26073 _this.setPosition(_this.gMapContext.marker.position);
26076 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26077 _this.fireEvent('mapClick', this, event);
26081 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26082 _this.fireEvent('mapRightClick', this, event);
26086 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26087 _this.fireEvent('markerClick', this, event);
26091 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26092 _this.fireEvent('markerRightClick', this, event);
26096 this.setPosition(this.gMapContext.location);
26098 this.fireEvent('initial', this, this.gMapContext.location);
26101 initOverlayView: function()
26105 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26109 _this.fireEvent('OverlayViewDraw', _this);
26114 _this.fireEvent('OverlayViewOnAdd', _this);
26117 onRemove: function()
26119 _this.fireEvent('OverlayViewOnRemove', _this);
26122 show: function(cpx)
26124 _this.fireEvent('OverlayViewShow', _this, cpx);
26129 _this.fireEvent('OverlayViewHide', _this);
26135 fromLatLngToContainerPixel: function(event)
26137 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26140 isApplied: function()
26142 return this.getGmapContext() == false ? false : true;
26145 getGmapContext: function()
26147 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26150 GMapContext: function()
26152 var position = new google.maps.LatLng(this.latitude, this.longitude);
26154 var _map = new google.maps.Map(this.el.dom, {
26157 mapTypeId: this.mapTypeId,
26158 mapTypeControl: this.mapTypeControl,
26159 disableDoubleClickZoom: this.disableDoubleClickZoom,
26160 scrollwheel: this.scrollwheel,
26161 streetViewControl: this.streetViewControl,
26162 locationName: this.locationName,
26163 draggable: this.draggable,
26164 enableAutocomplete: this.enableAutocomplete,
26165 enableReverseGeocode: this.enableReverseGeocode
26168 var _marker = new google.maps.Marker({
26169 position: position,
26171 title: this.markerTitle,
26172 draggable: this.draggable
26179 location: position,
26180 radius: this.radius,
26181 locationName: this.locationName,
26182 addressComponents: {
26183 formatted_address: null,
26184 addressLine1: null,
26185 addressLine2: null,
26187 streetNumber: null,
26191 stateOrProvince: null
26194 domContainer: this.el.dom,
26195 geodecoder: new google.maps.Geocoder()
26199 drawCircle: function(center, radius, options)
26201 if (this.gMapContext.circle != null) {
26202 this.gMapContext.circle.setMap(null);
26206 options = Roo.apply({}, options, {
26207 strokeColor: "#0000FF",
26208 strokeOpacity: .35,
26210 fillColor: "#0000FF",
26214 options.map = this.gMapContext.map;
26215 options.radius = radius;
26216 options.center = center;
26217 this.gMapContext.circle = new google.maps.Circle(options);
26218 return this.gMapContext.circle;
26224 setPosition: function(location)
26226 this.gMapContext.location = location;
26227 this.gMapContext.marker.setPosition(location);
26228 this.gMapContext.map.panTo(location);
26229 this.drawCircle(location, this.gMapContext.radius, {});
26233 if (this.gMapContext.settings.enableReverseGeocode) {
26234 this.gMapContext.geodecoder.geocode({
26235 latLng: this.gMapContext.location
26236 }, function(results, status) {
26238 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26239 _this.gMapContext.locationName = results[0].formatted_address;
26240 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26242 _this.fireEvent('positionchanged', this, location);
26249 this.fireEvent('positionchanged', this, location);
26254 google.maps.event.trigger(this.gMapContext.map, "resize");
26256 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26258 this.fireEvent('resize', this);
26261 setPositionByLatLng: function(latitude, longitude)
26263 this.setPosition(new google.maps.LatLng(latitude, longitude));
26266 getCurrentPosition: function()
26269 latitude: this.gMapContext.location.lat(),
26270 longitude: this.gMapContext.location.lng()
26274 getAddressName: function()
26276 return this.gMapContext.locationName;
26279 getAddressComponents: function()
26281 return this.gMapContext.addressComponents;
26284 address_component_from_google_geocode: function(address_components)
26288 for (var i = 0; i < address_components.length; i++) {
26289 var component = address_components[i];
26290 if (component.types.indexOf("postal_code") >= 0) {
26291 result.postalCode = component.short_name;
26292 } else if (component.types.indexOf("street_number") >= 0) {
26293 result.streetNumber = component.short_name;
26294 } else if (component.types.indexOf("route") >= 0) {
26295 result.streetName = component.short_name;
26296 } else if (component.types.indexOf("neighborhood") >= 0) {
26297 result.city = component.short_name;
26298 } else if (component.types.indexOf("locality") >= 0) {
26299 result.city = component.short_name;
26300 } else if (component.types.indexOf("sublocality") >= 0) {
26301 result.district = component.short_name;
26302 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26303 result.stateOrProvince = component.short_name;
26304 } else if (component.types.indexOf("country") >= 0) {
26305 result.country = component.short_name;
26309 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26310 result.addressLine2 = "";
26314 setZoomLevel: function(zoom)
26316 this.gMapContext.map.setZoom(zoom);
26329 this.fireEvent('show', this);
26340 this.fireEvent('hide', this);
26345 Roo.apply(Roo.bootstrap.LocationPicker, {
26347 OverlayView : function(map, options)
26349 options = options || {};
26363 * @class Roo.bootstrap.Alert
26364 * @extends Roo.bootstrap.Component
26365 * Bootstrap Alert class
26366 * @cfg {String} title The title of alert
26367 * @cfg {String} html The content of alert
26368 * @cfg {String} weight ( success | info | warning | danger )
26369 * @cfg {String} faicon font-awesomeicon
26372 * Create a new alert
26373 * @param {Object} config The config object
26377 Roo.bootstrap.Alert = function(config){
26378 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26382 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26389 getAutoCreate : function()
26398 cls : 'roo-alert-icon'
26403 cls : 'roo-alert-title',
26408 cls : 'roo-alert-text',
26415 cfg.cn[0].cls += ' fa ' + this.faicon;
26419 cfg.cls += ' alert-' + this.weight;
26425 initEvents: function()
26427 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26430 setTitle : function(str)
26432 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26435 setText : function(str)
26437 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26440 setWeight : function(weight)
26443 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26446 this.weight = weight;
26448 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26451 setIcon : function(icon)
26454 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26457 this.faicon = icon;
26459 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26480 * @class Roo.bootstrap.UploadCropbox
26481 * @extends Roo.bootstrap.Component
26482 * Bootstrap UploadCropbox class
26483 * @cfg {String} emptyText show when image has been loaded
26484 * @cfg {String} rotateNotify show when image too small to rotate
26485 * @cfg {Number} errorTimeout default 3000
26486 * @cfg {Number} minWidth default 300
26487 * @cfg {Number} minHeight default 300
26488 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26489 * @cfg {Boolean} isDocument (true|false) default false
26490 * @cfg {String} url action url
26491 * @cfg {String} paramName default 'imageUpload'
26492 * @cfg {String} method default POST
26493 * @cfg {Boolean} loadMask (true|false) default true
26494 * @cfg {Boolean} loadingText default 'Loading...'
26497 * Create a new UploadCropbox
26498 * @param {Object} config The config object
26501 Roo.bootstrap.UploadCropbox = function(config){
26502 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26506 * @event beforeselectfile
26507 * Fire before select file
26508 * @param {Roo.bootstrap.UploadCropbox} this
26510 "beforeselectfile" : true,
26513 * Fire after initEvent
26514 * @param {Roo.bootstrap.UploadCropbox} this
26519 * Fire after initEvent
26520 * @param {Roo.bootstrap.UploadCropbox} this
26521 * @param {String} data
26526 * Fire when preparing the file data
26527 * @param {Roo.bootstrap.UploadCropbox} this
26528 * @param {Object} file
26533 * Fire when get exception
26534 * @param {Roo.bootstrap.UploadCropbox} this
26535 * @param {XMLHttpRequest} xhr
26537 "exception" : true,
26539 * @event beforeloadcanvas
26540 * Fire before load the canvas
26541 * @param {Roo.bootstrap.UploadCropbox} this
26542 * @param {String} src
26544 "beforeloadcanvas" : true,
26547 * Fire when trash image
26548 * @param {Roo.bootstrap.UploadCropbox} this
26553 * Fire when download the image
26554 * @param {Roo.bootstrap.UploadCropbox} this
26558 * @event footerbuttonclick
26559 * Fire when footerbuttonclick
26560 * @param {Roo.bootstrap.UploadCropbox} this
26561 * @param {String} type
26563 "footerbuttonclick" : true,
26567 * @param {Roo.bootstrap.UploadCropbox} this
26572 * Fire when rotate the image
26573 * @param {Roo.bootstrap.UploadCropbox} this
26574 * @param {String} pos
26579 * Fire when inspect the file
26580 * @param {Roo.bootstrap.UploadCropbox} this
26581 * @param {Object} file
26586 * Fire when xhr upload the file
26587 * @param {Roo.bootstrap.UploadCropbox} this
26588 * @param {Object} data
26593 * Fire when arrange the file data
26594 * @param {Roo.bootstrap.UploadCropbox} this
26595 * @param {Object} formData
26600 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26603 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26605 emptyText : 'Click to upload image',
26606 rotateNotify : 'Image is too small to rotate',
26607 errorTimeout : 3000,
26621 cropType : 'image/jpeg',
26623 canvasLoaded : false,
26624 isDocument : false,
26626 paramName : 'imageUpload',
26628 loadingText : 'Loading...',
26631 getAutoCreate : function()
26635 cls : 'roo-upload-cropbox',
26639 cls : 'roo-upload-cropbox-selector',
26644 cls : 'roo-upload-cropbox-body',
26645 style : 'cursor:pointer',
26649 cls : 'roo-upload-cropbox-preview'
26653 cls : 'roo-upload-cropbox-thumb'
26657 cls : 'roo-upload-cropbox-empty-notify',
26658 html : this.emptyText
26662 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26663 html : this.rotateNotify
26669 cls : 'roo-upload-cropbox-footer',
26672 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26682 onRender : function(ct, position)
26684 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26686 if (this.buttons.length) {
26688 Roo.each(this.buttons, function(bb) {
26690 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26692 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26698 this.maskEl = this.el;
26702 initEvents : function()
26704 this.urlAPI = (window.createObjectURL && window) ||
26705 (window.URL && URL.revokeObjectURL && URL) ||
26706 (window.webkitURL && webkitURL);
26708 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26709 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26711 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26712 this.selectorEl.hide();
26714 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26715 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26717 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26718 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26719 this.thumbEl.hide();
26721 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26722 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26724 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26725 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26726 this.errorEl.hide();
26728 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26729 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26730 this.footerEl.hide();
26732 this.setThumbBoxSize();
26738 this.fireEvent('initial', this);
26745 window.addEventListener("resize", function() { _this.resize(); } );
26747 this.bodyEl.on('click', this.beforeSelectFile, this);
26750 this.bodyEl.on('touchstart', this.onTouchStart, this);
26751 this.bodyEl.on('touchmove', this.onTouchMove, this);
26752 this.bodyEl.on('touchend', this.onTouchEnd, this);
26756 this.bodyEl.on('mousedown', this.onMouseDown, this);
26757 this.bodyEl.on('mousemove', this.onMouseMove, this);
26758 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26759 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26760 Roo.get(document).on('mouseup', this.onMouseUp, this);
26763 this.selectorEl.on('change', this.onFileSelected, this);
26769 this.baseScale = 1;
26771 this.baseRotate = 1;
26772 this.dragable = false;
26773 this.pinching = false;
26776 this.cropData = false;
26777 this.notifyEl.dom.innerHTML = this.emptyText;
26779 this.selectorEl.dom.value = '';
26783 resize : function()
26785 if(this.fireEvent('resize', this) != false){
26786 this.setThumbBoxPosition();
26787 this.setCanvasPosition();
26791 onFooterButtonClick : function(e, el, o, type)
26794 case 'rotate-left' :
26795 this.onRotateLeft(e);
26797 case 'rotate-right' :
26798 this.onRotateRight(e);
26801 this.beforeSelectFile(e);
26816 this.fireEvent('footerbuttonclick', this, type);
26819 beforeSelectFile : function(e)
26821 e.preventDefault();
26823 if(this.fireEvent('beforeselectfile', this) != false){
26824 this.selectorEl.dom.click();
26828 onFileSelected : function(e)
26830 e.preventDefault();
26832 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26836 var file = this.selectorEl.dom.files[0];
26838 if(this.fireEvent('inspect', this, file) != false){
26839 this.prepare(file);
26844 trash : function(e)
26846 this.fireEvent('trash', this);
26849 download : function(e)
26851 this.fireEvent('download', this);
26854 loadCanvas : function(src)
26856 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26860 this.imageEl = document.createElement('img');
26864 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26866 this.imageEl.src = src;
26870 onLoadCanvas : function()
26872 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26873 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26875 this.bodyEl.un('click', this.beforeSelectFile, this);
26877 this.notifyEl.hide();
26878 this.thumbEl.show();
26879 this.footerEl.show();
26881 this.baseRotateLevel();
26883 if(this.isDocument){
26884 this.setThumbBoxSize();
26887 this.setThumbBoxPosition();
26889 this.baseScaleLevel();
26895 this.canvasLoaded = true;
26898 this.maskEl.unmask();
26903 setCanvasPosition : function()
26905 if(!this.canvasEl){
26909 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26910 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26912 this.previewEl.setLeft(pw);
26913 this.previewEl.setTop(ph);
26917 onMouseDown : function(e)
26921 this.dragable = true;
26922 this.pinching = false;
26924 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26925 this.dragable = false;
26929 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26930 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26934 onMouseMove : function(e)
26938 if(!this.canvasLoaded){
26942 if (!this.dragable){
26946 var minX = Math.ceil(this.thumbEl.getLeft(true));
26947 var minY = Math.ceil(this.thumbEl.getTop(true));
26949 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26950 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26952 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26953 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26955 x = x - this.mouseX;
26956 y = y - this.mouseY;
26958 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26959 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26961 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26962 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26964 this.previewEl.setLeft(bgX);
26965 this.previewEl.setTop(bgY);
26967 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26968 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26971 onMouseUp : function(e)
26975 this.dragable = false;
26978 onMouseWheel : function(e)
26982 this.startScale = this.scale;
26984 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26986 if(!this.zoomable()){
26987 this.scale = this.startScale;
26996 zoomable : function()
26998 var minScale = this.thumbEl.getWidth() / this.minWidth;
27000 if(this.minWidth < this.minHeight){
27001 minScale = this.thumbEl.getHeight() / this.minHeight;
27004 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27005 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27009 (this.rotate == 0 || this.rotate == 180) &&
27011 width > this.imageEl.OriginWidth ||
27012 height > this.imageEl.OriginHeight ||
27013 (width < this.minWidth && height < this.minHeight)
27021 (this.rotate == 90 || this.rotate == 270) &&
27023 width > this.imageEl.OriginWidth ||
27024 height > this.imageEl.OriginHeight ||
27025 (width < this.minHeight && height < this.minWidth)
27032 !this.isDocument &&
27033 (this.rotate == 0 || this.rotate == 180) &&
27035 width < this.minWidth ||
27036 width > this.imageEl.OriginWidth ||
27037 height < this.minHeight ||
27038 height > this.imageEl.OriginHeight
27045 !this.isDocument &&
27046 (this.rotate == 90 || this.rotate == 270) &&
27048 width < this.minHeight ||
27049 width > this.imageEl.OriginWidth ||
27050 height < this.minWidth ||
27051 height > this.imageEl.OriginHeight
27061 onRotateLeft : function(e)
27063 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27065 var minScale = this.thumbEl.getWidth() / this.minWidth;
27067 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27068 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27070 this.startScale = this.scale;
27072 while (this.getScaleLevel() < minScale){
27074 this.scale = this.scale + 1;
27076 if(!this.zoomable()){
27081 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27082 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27087 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27094 this.scale = this.startScale;
27096 this.onRotateFail();
27101 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27103 if(this.isDocument){
27104 this.setThumbBoxSize();
27105 this.setThumbBoxPosition();
27106 this.setCanvasPosition();
27111 this.fireEvent('rotate', this, 'left');
27115 onRotateRight : function(e)
27117 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27119 var minScale = this.thumbEl.getWidth() / this.minWidth;
27121 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27122 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27124 this.startScale = this.scale;
27126 while (this.getScaleLevel() < minScale){
27128 this.scale = this.scale + 1;
27130 if(!this.zoomable()){
27135 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27136 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27141 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27148 this.scale = this.startScale;
27150 this.onRotateFail();
27155 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27157 if(this.isDocument){
27158 this.setThumbBoxSize();
27159 this.setThumbBoxPosition();
27160 this.setCanvasPosition();
27165 this.fireEvent('rotate', this, 'right');
27168 onRotateFail : function()
27170 this.errorEl.show(true);
27174 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27179 this.previewEl.dom.innerHTML = '';
27181 var canvasEl = document.createElement("canvas");
27183 var contextEl = canvasEl.getContext("2d");
27185 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27186 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27187 var center = this.imageEl.OriginWidth / 2;
27189 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27190 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27191 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27192 center = this.imageEl.OriginHeight / 2;
27195 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27197 contextEl.translate(center, center);
27198 contextEl.rotate(this.rotate * Math.PI / 180);
27200 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27202 this.canvasEl = document.createElement("canvas");
27204 this.contextEl = this.canvasEl.getContext("2d");
27206 switch (this.rotate) {
27209 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27210 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27212 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27217 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27218 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27220 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27221 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);
27225 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27230 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27231 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27233 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27234 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);
27238 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);
27243 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27244 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27246 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27247 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27251 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);
27258 this.previewEl.appendChild(this.canvasEl);
27260 this.setCanvasPosition();
27265 if(!this.canvasLoaded){
27269 var imageCanvas = document.createElement("canvas");
27271 var imageContext = imageCanvas.getContext("2d");
27273 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27274 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27276 var center = imageCanvas.width / 2;
27278 imageContext.translate(center, center);
27280 imageContext.rotate(this.rotate * Math.PI / 180);
27282 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27284 var canvas = document.createElement("canvas");
27286 var context = canvas.getContext("2d");
27288 canvas.width = this.minWidth;
27289 canvas.height = this.minHeight;
27291 switch (this.rotate) {
27294 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27295 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27297 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27298 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27300 var targetWidth = this.minWidth - 2 * x;
27301 var targetHeight = this.minHeight - 2 * y;
27305 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27306 scale = targetWidth / width;
27309 if(x > 0 && y == 0){
27310 scale = targetHeight / height;
27313 if(x > 0 && y > 0){
27314 scale = targetWidth / width;
27316 if(width < height){
27317 scale = targetHeight / height;
27321 context.scale(scale, scale);
27323 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27324 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27326 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27327 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27329 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27334 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27335 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27337 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27338 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27340 var targetWidth = this.minWidth - 2 * x;
27341 var targetHeight = this.minHeight - 2 * y;
27345 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27346 scale = targetWidth / width;
27349 if(x > 0 && y == 0){
27350 scale = targetHeight / height;
27353 if(x > 0 && y > 0){
27354 scale = targetWidth / width;
27356 if(width < height){
27357 scale = targetHeight / height;
27361 context.scale(scale, scale);
27363 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27364 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27366 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27367 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27369 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27371 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27376 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27377 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27379 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27380 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27382 var targetWidth = this.minWidth - 2 * x;
27383 var targetHeight = this.minHeight - 2 * y;
27387 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27388 scale = targetWidth / width;
27391 if(x > 0 && y == 0){
27392 scale = targetHeight / height;
27395 if(x > 0 && y > 0){
27396 scale = targetWidth / width;
27398 if(width < height){
27399 scale = targetHeight / height;
27403 context.scale(scale, scale);
27405 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27406 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27408 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27409 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27411 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27412 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27414 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27419 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27420 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27422 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27423 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27425 var targetWidth = this.minWidth - 2 * x;
27426 var targetHeight = this.minHeight - 2 * y;
27430 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27431 scale = targetWidth / width;
27434 if(x > 0 && y == 0){
27435 scale = targetHeight / height;
27438 if(x > 0 && y > 0){
27439 scale = targetWidth / width;
27441 if(width < height){
27442 scale = targetHeight / height;
27446 context.scale(scale, scale);
27448 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27449 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27451 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27452 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27454 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27456 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27463 this.cropData = canvas.toDataURL(this.cropType);
27465 if(this.fireEvent('crop', this, this.cropData) !== false){
27466 this.process(this.file, this.cropData);
27473 setThumbBoxSize : function()
27477 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27478 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27479 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27481 this.minWidth = width;
27482 this.minHeight = height;
27484 if(this.rotate == 90 || this.rotate == 270){
27485 this.minWidth = height;
27486 this.minHeight = width;
27491 width = Math.ceil(this.minWidth * height / this.minHeight);
27493 if(this.minWidth > this.minHeight){
27495 height = Math.ceil(this.minHeight * width / this.minWidth);
27498 this.thumbEl.setStyle({
27499 width : width + 'px',
27500 height : height + 'px'
27507 setThumbBoxPosition : function()
27509 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27510 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27512 this.thumbEl.setLeft(x);
27513 this.thumbEl.setTop(y);
27517 baseRotateLevel : function()
27519 this.baseRotate = 1;
27522 typeof(this.exif) != 'undefined' &&
27523 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27524 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27526 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27529 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27533 baseScaleLevel : function()
27537 if(this.isDocument){
27539 if(this.baseRotate == 6 || this.baseRotate == 8){
27541 height = this.thumbEl.getHeight();
27542 this.baseScale = height / this.imageEl.OriginWidth;
27544 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27545 width = this.thumbEl.getWidth();
27546 this.baseScale = width / this.imageEl.OriginHeight;
27552 height = this.thumbEl.getHeight();
27553 this.baseScale = height / this.imageEl.OriginHeight;
27555 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27556 width = this.thumbEl.getWidth();
27557 this.baseScale = width / this.imageEl.OriginWidth;
27563 if(this.baseRotate == 6 || this.baseRotate == 8){
27565 width = this.thumbEl.getHeight();
27566 this.baseScale = width / this.imageEl.OriginHeight;
27568 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27569 height = this.thumbEl.getWidth();
27570 this.baseScale = height / this.imageEl.OriginHeight;
27573 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27574 height = this.thumbEl.getWidth();
27575 this.baseScale = height / this.imageEl.OriginHeight;
27577 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27578 width = this.thumbEl.getHeight();
27579 this.baseScale = width / this.imageEl.OriginWidth;
27586 width = this.thumbEl.getWidth();
27587 this.baseScale = width / this.imageEl.OriginWidth;
27589 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27590 height = this.thumbEl.getHeight();
27591 this.baseScale = height / this.imageEl.OriginHeight;
27594 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27596 height = this.thumbEl.getHeight();
27597 this.baseScale = height / this.imageEl.OriginHeight;
27599 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27600 width = this.thumbEl.getWidth();
27601 this.baseScale = width / this.imageEl.OriginWidth;
27609 getScaleLevel : function()
27611 return this.baseScale * Math.pow(1.1, this.scale);
27614 onTouchStart : function(e)
27616 if(!this.canvasLoaded){
27617 this.beforeSelectFile(e);
27621 var touches = e.browserEvent.touches;
27627 if(touches.length == 1){
27628 this.onMouseDown(e);
27632 if(touches.length != 2){
27638 for(var i = 0, finger; finger = touches[i]; i++){
27639 coords.push(finger.pageX, finger.pageY);
27642 var x = Math.pow(coords[0] - coords[2], 2);
27643 var y = Math.pow(coords[1] - coords[3], 2);
27645 this.startDistance = Math.sqrt(x + y);
27647 this.startScale = this.scale;
27649 this.pinching = true;
27650 this.dragable = false;
27654 onTouchMove : function(e)
27656 if(!this.pinching && !this.dragable){
27660 var touches = e.browserEvent.touches;
27667 this.onMouseMove(e);
27673 for(var i = 0, finger; finger = touches[i]; i++){
27674 coords.push(finger.pageX, finger.pageY);
27677 var x = Math.pow(coords[0] - coords[2], 2);
27678 var y = Math.pow(coords[1] - coords[3], 2);
27680 this.endDistance = Math.sqrt(x + y);
27682 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27684 if(!this.zoomable()){
27685 this.scale = this.startScale;
27693 onTouchEnd : function(e)
27695 this.pinching = false;
27696 this.dragable = false;
27700 process : function(file, crop)
27703 this.maskEl.mask(this.loadingText);
27706 this.xhr = new XMLHttpRequest();
27708 file.xhr = this.xhr;
27710 this.xhr.open(this.method, this.url, true);
27713 "Accept": "application/json",
27714 "Cache-Control": "no-cache",
27715 "X-Requested-With": "XMLHttpRequest"
27718 for (var headerName in headers) {
27719 var headerValue = headers[headerName];
27721 this.xhr.setRequestHeader(headerName, headerValue);
27727 this.xhr.onload = function()
27729 _this.xhrOnLoad(_this.xhr);
27732 this.xhr.onerror = function()
27734 _this.xhrOnError(_this.xhr);
27737 var formData = new FormData();
27739 formData.append('returnHTML', 'NO');
27742 formData.append('crop', crop);
27745 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27746 formData.append(this.paramName, file, file.name);
27749 if(typeof(file.filename) != 'undefined'){
27750 formData.append('filename', file.filename);
27753 if(typeof(file.mimetype) != 'undefined'){
27754 formData.append('mimetype', file.mimetype);
27757 if(this.fireEvent('arrange', this, formData) != false){
27758 this.xhr.send(formData);
27762 xhrOnLoad : function(xhr)
27765 this.maskEl.unmask();
27768 if (xhr.readyState !== 4) {
27769 this.fireEvent('exception', this, xhr);
27773 var response = Roo.decode(xhr.responseText);
27775 if(!response.success){
27776 this.fireEvent('exception', this, xhr);
27780 var response = Roo.decode(xhr.responseText);
27782 this.fireEvent('upload', this, response);
27786 xhrOnError : function()
27789 this.maskEl.unmask();
27792 Roo.log('xhr on error');
27794 var response = Roo.decode(xhr.responseText);
27800 prepare : function(file)
27803 this.maskEl.mask(this.loadingText);
27809 if(typeof(file) === 'string'){
27810 this.loadCanvas(file);
27814 if(!file || !this.urlAPI){
27819 this.cropType = file.type;
27823 if(this.fireEvent('prepare', this, this.file) != false){
27825 var reader = new FileReader();
27827 reader.onload = function (e) {
27828 if (e.target.error) {
27829 Roo.log(e.target.error);
27833 var buffer = e.target.result,
27834 dataView = new DataView(buffer),
27836 maxOffset = dataView.byteLength - 4,
27840 if (dataView.getUint16(0) === 0xffd8) {
27841 while (offset < maxOffset) {
27842 markerBytes = dataView.getUint16(offset);
27844 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27845 markerLength = dataView.getUint16(offset + 2) + 2;
27846 if (offset + markerLength > dataView.byteLength) {
27847 Roo.log('Invalid meta data: Invalid segment size.');
27851 if(markerBytes == 0xffe1){
27852 _this.parseExifData(
27859 offset += markerLength;
27869 var url = _this.urlAPI.createObjectURL(_this.file);
27871 _this.loadCanvas(url);
27876 reader.readAsArrayBuffer(this.file);
27882 parseExifData : function(dataView, offset, length)
27884 var tiffOffset = offset + 10,
27888 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27889 // No Exif data, might be XMP data instead
27893 // Check for the ASCII code for "Exif" (0x45786966):
27894 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27895 // No Exif data, might be XMP data instead
27898 if (tiffOffset + 8 > dataView.byteLength) {
27899 Roo.log('Invalid Exif data: Invalid segment size.');
27902 // Check for the two null bytes:
27903 if (dataView.getUint16(offset + 8) !== 0x0000) {
27904 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27907 // Check the byte alignment:
27908 switch (dataView.getUint16(tiffOffset)) {
27910 littleEndian = true;
27913 littleEndian = false;
27916 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27919 // Check for the TIFF tag marker (0x002A):
27920 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27921 Roo.log('Invalid Exif data: Missing TIFF marker.');
27924 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27925 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27927 this.parseExifTags(
27930 tiffOffset + dirOffset,
27935 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27940 if (dirOffset + 6 > dataView.byteLength) {
27941 Roo.log('Invalid Exif data: Invalid directory offset.');
27944 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27945 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27946 if (dirEndOffset + 4 > dataView.byteLength) {
27947 Roo.log('Invalid Exif data: Invalid directory size.');
27950 for (i = 0; i < tagsNumber; i += 1) {
27954 dirOffset + 2 + 12 * i, // tag offset
27958 // Return the offset to the next directory:
27959 return dataView.getUint32(dirEndOffset, littleEndian);
27962 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27964 var tag = dataView.getUint16(offset, littleEndian);
27966 this.exif[tag] = this.getExifValue(
27970 dataView.getUint16(offset + 2, littleEndian), // tag type
27971 dataView.getUint32(offset + 4, littleEndian), // tag length
27976 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27978 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27987 Roo.log('Invalid Exif data: Invalid tag type.');
27991 tagSize = tagType.size * length;
27992 // Determine if the value is contained in the dataOffset bytes,
27993 // or if the value at the dataOffset is a pointer to the actual data:
27994 dataOffset = tagSize > 4 ?
27995 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27996 if (dataOffset + tagSize > dataView.byteLength) {
27997 Roo.log('Invalid Exif data: Invalid data offset.');
28000 if (length === 1) {
28001 return tagType.getValue(dataView, dataOffset, littleEndian);
28004 for (i = 0; i < length; i += 1) {
28005 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28008 if (tagType.ascii) {
28010 // Concatenate the chars:
28011 for (i = 0; i < values.length; i += 1) {
28013 // Ignore the terminating NULL byte(s):
28014 if (c === '\u0000') {
28026 Roo.apply(Roo.bootstrap.UploadCropbox, {
28028 'Orientation': 0x0112
28032 1: 0, //'top-left',
28034 3: 180, //'bottom-right',
28035 // 4: 'bottom-left',
28037 6: 90, //'right-top',
28038 // 7: 'right-bottom',
28039 8: 270 //'left-bottom'
28043 // byte, 8-bit unsigned int:
28045 getValue: function (dataView, dataOffset) {
28046 return dataView.getUint8(dataOffset);
28050 // ascii, 8-bit byte:
28052 getValue: function (dataView, dataOffset) {
28053 return String.fromCharCode(dataView.getUint8(dataOffset));
28058 // short, 16 bit int:
28060 getValue: function (dataView, dataOffset, littleEndian) {
28061 return dataView.getUint16(dataOffset, littleEndian);
28065 // long, 32 bit int:
28067 getValue: function (dataView, dataOffset, littleEndian) {
28068 return dataView.getUint32(dataOffset, littleEndian);
28072 // rational = two long values, first is numerator, second is denominator:
28074 getValue: function (dataView, dataOffset, littleEndian) {
28075 return dataView.getUint32(dataOffset, littleEndian) /
28076 dataView.getUint32(dataOffset + 4, littleEndian);
28080 // slong, 32 bit signed int:
28082 getValue: function (dataView, dataOffset, littleEndian) {
28083 return dataView.getInt32(dataOffset, littleEndian);
28087 // srational, two slongs, first is numerator, second is denominator:
28089 getValue: function (dataView, dataOffset, littleEndian) {
28090 return dataView.getInt32(dataOffset, littleEndian) /
28091 dataView.getInt32(dataOffset + 4, littleEndian);
28101 cls : 'btn-group roo-upload-cropbox-rotate-left',
28102 action : 'rotate-left',
28106 cls : 'btn btn-default',
28107 html : '<i class="fa fa-undo"></i>'
28113 cls : 'btn-group roo-upload-cropbox-picture',
28114 action : 'picture',
28118 cls : 'btn btn-default',
28119 html : '<i class="fa fa-picture-o"></i>'
28125 cls : 'btn-group roo-upload-cropbox-rotate-right',
28126 action : 'rotate-right',
28130 cls : 'btn btn-default',
28131 html : '<i class="fa fa-repeat"></i>'
28139 cls : 'btn-group roo-upload-cropbox-rotate-left',
28140 action : 'rotate-left',
28144 cls : 'btn btn-default',
28145 html : '<i class="fa fa-undo"></i>'
28151 cls : 'btn-group roo-upload-cropbox-download',
28152 action : 'download',
28156 cls : 'btn btn-default',
28157 html : '<i class="fa fa-download"></i>'
28163 cls : 'btn-group roo-upload-cropbox-crop',
28168 cls : 'btn btn-default',
28169 html : '<i class="fa fa-crop"></i>'
28175 cls : 'btn-group roo-upload-cropbox-trash',
28180 cls : 'btn btn-default',
28181 html : '<i class="fa fa-trash"></i>'
28187 cls : 'btn-group roo-upload-cropbox-rotate-right',
28188 action : 'rotate-right',
28192 cls : 'btn btn-default',
28193 html : '<i class="fa fa-repeat"></i>'
28201 cls : 'btn-group roo-upload-cropbox-rotate-left',
28202 action : 'rotate-left',
28206 cls : 'btn btn-default',
28207 html : '<i class="fa fa-undo"></i>'
28213 cls : 'btn-group roo-upload-cropbox-rotate-right',
28214 action : 'rotate-right',
28218 cls : 'btn btn-default',
28219 html : '<i class="fa fa-repeat"></i>'
28232 * @class Roo.bootstrap.DocumentManager
28233 * @extends Roo.bootstrap.Component
28234 * Bootstrap DocumentManager class
28235 * @cfg {String} paramName default 'imageUpload'
28236 * @cfg {String} toolTipName default 'filename'
28237 * @cfg {String} method default POST
28238 * @cfg {String} url action url
28239 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28240 * @cfg {Boolean} multiple multiple upload default true
28241 * @cfg {Number} thumbSize default 300
28242 * @cfg {String} fieldLabel
28243 * @cfg {Number} labelWidth default 4
28244 * @cfg {String} labelAlign (left|top) default left
28245 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28246 * @cfg {Number} labellg set the width of label (1-12)
28247 * @cfg {Number} labelmd set the width of label (1-12)
28248 * @cfg {Number} labelsm set the width of label (1-12)
28249 * @cfg {Number} labelxs set the width of label (1-12)
28252 * Create a new DocumentManager
28253 * @param {Object} config The config object
28256 Roo.bootstrap.DocumentManager = function(config){
28257 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28260 this.delegates = [];
28265 * Fire when initial the DocumentManager
28266 * @param {Roo.bootstrap.DocumentManager} this
28271 * inspect selected file
28272 * @param {Roo.bootstrap.DocumentManager} this
28273 * @param {File} file
28278 * Fire when xhr load exception
28279 * @param {Roo.bootstrap.DocumentManager} this
28280 * @param {XMLHttpRequest} xhr
28282 "exception" : true,
28284 * @event afterupload
28285 * Fire when xhr load exception
28286 * @param {Roo.bootstrap.DocumentManager} this
28287 * @param {XMLHttpRequest} xhr
28289 "afterupload" : true,
28292 * prepare the form data
28293 * @param {Roo.bootstrap.DocumentManager} this
28294 * @param {Object} formData
28299 * Fire when remove the file
28300 * @param {Roo.bootstrap.DocumentManager} this
28301 * @param {Object} file
28306 * Fire after refresh the file
28307 * @param {Roo.bootstrap.DocumentManager} this
28312 * Fire after click the image
28313 * @param {Roo.bootstrap.DocumentManager} this
28314 * @param {Object} file
28319 * Fire when upload a image and editable set to true
28320 * @param {Roo.bootstrap.DocumentManager} this
28321 * @param {Object} file
28325 * @event beforeselectfile
28326 * Fire before select file
28327 * @param {Roo.bootstrap.DocumentManager} this
28329 "beforeselectfile" : true,
28332 * Fire before process file
28333 * @param {Roo.bootstrap.DocumentManager} this
28334 * @param {Object} file
28341 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28350 paramName : 'imageUpload',
28351 toolTipName : 'filename',
28354 labelAlign : 'left',
28364 getAutoCreate : function()
28366 var managerWidget = {
28368 cls : 'roo-document-manager',
28372 cls : 'roo-document-manager-selector',
28377 cls : 'roo-document-manager-uploader',
28381 cls : 'roo-document-manager-upload-btn',
28382 html : '<i class="fa fa-plus"></i>'
28393 cls : 'column col-md-12',
28398 if(this.fieldLabel.length){
28403 cls : 'column col-md-12',
28404 html : this.fieldLabel
28408 cls : 'column col-md-12',
28413 if(this.labelAlign == 'left'){
28418 html : this.fieldLabel
28427 if(this.labelWidth > 12){
28428 content[0].style = "width: " + this.labelWidth + 'px';
28431 if(this.labelWidth < 13 && this.labelmd == 0){
28432 this.labelmd = this.labelWidth;
28435 if(this.labellg > 0){
28436 content[0].cls += ' col-lg-' + this.labellg;
28437 content[1].cls += ' col-lg-' + (12 - this.labellg);
28440 if(this.labelmd > 0){
28441 content[0].cls += ' col-md-' + this.labelmd;
28442 content[1].cls += ' col-md-' + (12 - this.labelmd);
28445 if(this.labelsm > 0){
28446 content[0].cls += ' col-sm-' + this.labelsm;
28447 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28450 if(this.labelxs > 0){
28451 content[0].cls += ' col-xs-' + this.labelxs;
28452 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28460 cls : 'row clearfix',
28468 initEvents : function()
28470 this.managerEl = this.el.select('.roo-document-manager', true).first();
28471 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28473 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28474 this.selectorEl.hide();
28477 this.selectorEl.attr('multiple', 'multiple');
28480 this.selectorEl.on('change', this.onFileSelected, this);
28482 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28483 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28485 this.uploader.on('click', this.onUploaderClick, this);
28487 this.renderProgressDialog();
28491 window.addEventListener("resize", function() { _this.refresh(); } );
28493 this.fireEvent('initial', this);
28496 renderProgressDialog : function()
28500 this.progressDialog = new Roo.bootstrap.Modal({
28501 cls : 'roo-document-manager-progress-dialog',
28502 allow_close : false,
28512 btnclick : function() {
28513 _this.uploadCancel();
28519 this.progressDialog.render(Roo.get(document.body));
28521 this.progress = new Roo.bootstrap.Progress({
28522 cls : 'roo-document-manager-progress',
28527 this.progress.render(this.progressDialog.getChildContainer());
28529 this.progressBar = new Roo.bootstrap.ProgressBar({
28530 cls : 'roo-document-manager-progress-bar',
28533 aria_valuemax : 12,
28537 this.progressBar.render(this.progress.getChildContainer());
28540 onUploaderClick : function(e)
28542 e.preventDefault();
28544 if(this.fireEvent('beforeselectfile', this) != false){
28545 this.selectorEl.dom.click();
28550 onFileSelected : function(e)
28552 e.preventDefault();
28554 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28558 Roo.each(this.selectorEl.dom.files, function(file){
28559 if(this.fireEvent('inspect', this, file) != false){
28560 this.files.push(file);
28570 this.selectorEl.dom.value = '';
28572 if(!this.files.length){
28576 if(this.boxes > 0 && this.files.length > this.boxes){
28577 this.files = this.files.slice(0, this.boxes);
28580 this.uploader.show();
28582 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28583 this.uploader.hide();
28592 Roo.each(this.files, function(file){
28594 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28595 var f = this.renderPreview(file);
28600 if(file.type.indexOf('image') != -1){
28601 this.delegates.push(
28603 _this.process(file);
28604 }).createDelegate(this)
28612 _this.process(file);
28613 }).createDelegate(this)
28618 this.files = files;
28620 this.delegates = this.delegates.concat(docs);
28622 if(!this.delegates.length){
28627 this.progressBar.aria_valuemax = this.delegates.length;
28634 arrange : function()
28636 if(!this.delegates.length){
28637 this.progressDialog.hide();
28642 var delegate = this.delegates.shift();
28644 this.progressDialog.show();
28646 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28648 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28653 refresh : function()
28655 this.uploader.show();
28657 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28658 this.uploader.hide();
28661 Roo.isTouch ? this.closable(false) : this.closable(true);
28663 this.fireEvent('refresh', this);
28666 onRemove : function(e, el, o)
28668 e.preventDefault();
28670 this.fireEvent('remove', this, o);
28674 remove : function(o)
28678 Roo.each(this.files, function(file){
28679 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28688 this.files = files;
28695 Roo.each(this.files, function(file){
28700 file.target.remove();
28709 onClick : function(e, el, o)
28711 e.preventDefault();
28713 this.fireEvent('click', this, o);
28717 closable : function(closable)
28719 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28721 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28733 xhrOnLoad : function(xhr)
28735 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28739 if (xhr.readyState !== 4) {
28741 this.fireEvent('exception', this, xhr);
28745 var response = Roo.decode(xhr.responseText);
28747 if(!response.success){
28749 this.fireEvent('exception', this, xhr);
28753 var file = this.renderPreview(response.data);
28755 this.files.push(file);
28759 this.fireEvent('afterupload', this, xhr);
28763 xhrOnError : function(xhr)
28765 Roo.log('xhr on error');
28767 var response = Roo.decode(xhr.responseText);
28774 process : function(file)
28776 if(this.fireEvent('process', this, file) !== false){
28777 if(this.editable && file.type.indexOf('image') != -1){
28778 this.fireEvent('edit', this, file);
28782 this.uploadStart(file, false);
28789 uploadStart : function(file, crop)
28791 this.xhr = new XMLHttpRequest();
28793 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28798 file.xhr = this.xhr;
28800 this.managerEl.createChild({
28802 cls : 'roo-document-manager-loading',
28806 tooltip : file.name,
28807 cls : 'roo-document-manager-thumb',
28808 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28814 this.xhr.open(this.method, this.url, true);
28817 "Accept": "application/json",
28818 "Cache-Control": "no-cache",
28819 "X-Requested-With": "XMLHttpRequest"
28822 for (var headerName in headers) {
28823 var headerValue = headers[headerName];
28825 this.xhr.setRequestHeader(headerName, headerValue);
28831 this.xhr.onload = function()
28833 _this.xhrOnLoad(_this.xhr);
28836 this.xhr.onerror = function()
28838 _this.xhrOnError(_this.xhr);
28841 var formData = new FormData();
28843 formData.append('returnHTML', 'NO');
28846 formData.append('crop', crop);
28849 formData.append(this.paramName, file, file.name);
28856 if(this.fireEvent('prepare', this, formData, options) != false){
28858 if(options.manually){
28862 this.xhr.send(formData);
28866 this.uploadCancel();
28869 uploadCancel : function()
28875 this.delegates = [];
28877 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28884 renderPreview : function(file)
28886 if(typeof(file.target) != 'undefined' && file.target){
28890 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28892 var previewEl = this.managerEl.createChild({
28894 cls : 'roo-document-manager-preview',
28898 tooltip : file[this.toolTipName],
28899 cls : 'roo-document-manager-thumb',
28900 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28905 html : '<i class="fa fa-times-circle"></i>'
28910 var close = previewEl.select('button.close', true).first();
28912 close.on('click', this.onRemove, this, file);
28914 file.target = previewEl;
28916 var image = previewEl.select('img', true).first();
28920 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28922 image.on('click', this.onClick, this, file);
28928 onPreviewLoad : function(file, image)
28930 if(typeof(file.target) == 'undefined' || !file.target){
28934 var width = image.dom.naturalWidth || image.dom.width;
28935 var height = image.dom.naturalHeight || image.dom.height;
28937 if(width > height){
28938 file.target.addClass('wide');
28942 file.target.addClass('tall');
28947 uploadFromSource : function(file, crop)
28949 this.xhr = new XMLHttpRequest();
28951 this.managerEl.createChild({
28953 cls : 'roo-document-manager-loading',
28957 tooltip : file.name,
28958 cls : 'roo-document-manager-thumb',
28959 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28965 this.xhr.open(this.method, this.url, true);
28968 "Accept": "application/json",
28969 "Cache-Control": "no-cache",
28970 "X-Requested-With": "XMLHttpRequest"
28973 for (var headerName in headers) {
28974 var headerValue = headers[headerName];
28976 this.xhr.setRequestHeader(headerName, headerValue);
28982 this.xhr.onload = function()
28984 _this.xhrOnLoad(_this.xhr);
28987 this.xhr.onerror = function()
28989 _this.xhrOnError(_this.xhr);
28992 var formData = new FormData();
28994 formData.append('returnHTML', 'NO');
28996 formData.append('crop', crop);
28998 if(typeof(file.filename) != 'undefined'){
28999 formData.append('filename', file.filename);
29002 if(typeof(file.mimetype) != 'undefined'){
29003 formData.append('mimetype', file.mimetype);
29008 if(this.fireEvent('prepare', this, formData) != false){
29009 this.xhr.send(formData);
29019 * @class Roo.bootstrap.DocumentViewer
29020 * @extends Roo.bootstrap.Component
29021 * Bootstrap DocumentViewer class
29022 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29023 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29026 * Create a new DocumentViewer
29027 * @param {Object} config The config object
29030 Roo.bootstrap.DocumentViewer = function(config){
29031 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29036 * Fire after initEvent
29037 * @param {Roo.bootstrap.DocumentViewer} this
29043 * @param {Roo.bootstrap.DocumentViewer} this
29048 * Fire after download button
29049 * @param {Roo.bootstrap.DocumentViewer} this
29054 * Fire after trash button
29055 * @param {Roo.bootstrap.DocumentViewer} this
29062 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29064 showDownload : true,
29068 getAutoCreate : function()
29072 cls : 'roo-document-viewer',
29076 cls : 'roo-document-viewer-body',
29080 cls : 'roo-document-viewer-thumb',
29084 cls : 'roo-document-viewer-image'
29092 cls : 'roo-document-viewer-footer',
29095 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29099 cls : 'btn-group roo-document-viewer-download',
29103 cls : 'btn btn-default',
29104 html : '<i class="fa fa-download"></i>'
29110 cls : 'btn-group roo-document-viewer-trash',
29114 cls : 'btn btn-default',
29115 html : '<i class="fa fa-trash"></i>'
29128 initEvents : function()
29130 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29131 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29133 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29134 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29136 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29137 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29139 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29140 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29142 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29143 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29145 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29146 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29148 this.bodyEl.on('click', this.onClick, this);
29149 this.downloadBtn.on('click', this.onDownload, this);
29150 this.trashBtn.on('click', this.onTrash, this);
29152 this.downloadBtn.hide();
29153 this.trashBtn.hide();
29155 if(this.showDownload){
29156 this.downloadBtn.show();
29159 if(this.showTrash){
29160 this.trashBtn.show();
29163 if(!this.showDownload && !this.showTrash) {
29164 this.footerEl.hide();
29169 initial : function()
29171 this.fireEvent('initial', this);
29175 onClick : function(e)
29177 e.preventDefault();
29179 this.fireEvent('click', this);
29182 onDownload : function(e)
29184 e.preventDefault();
29186 this.fireEvent('download', this);
29189 onTrash : function(e)
29191 e.preventDefault();
29193 this.fireEvent('trash', this);
29205 * @class Roo.bootstrap.NavProgressBar
29206 * @extends Roo.bootstrap.Component
29207 * Bootstrap NavProgressBar class
29210 * Create a new nav progress bar
29211 * @param {Object} config The config object
29214 Roo.bootstrap.NavProgressBar = function(config){
29215 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29217 this.bullets = this.bullets || [];
29219 // Roo.bootstrap.NavProgressBar.register(this);
29223 * Fires when the active item changes
29224 * @param {Roo.bootstrap.NavProgressBar} this
29225 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29226 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29233 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29238 getAutoCreate : function()
29240 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29244 cls : 'roo-navigation-bar-group',
29248 cls : 'roo-navigation-top-bar'
29252 cls : 'roo-navigation-bullets-bar',
29256 cls : 'roo-navigation-bar'
29263 cls : 'roo-navigation-bottom-bar'
29273 initEvents: function()
29278 onRender : function(ct, position)
29280 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29282 if(this.bullets.length){
29283 Roo.each(this.bullets, function(b){
29292 addItem : function(cfg)
29294 var item = new Roo.bootstrap.NavProgressItem(cfg);
29296 item.parentId = this.id;
29297 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29300 var top = new Roo.bootstrap.Element({
29302 cls : 'roo-navigation-bar-text'
29305 var bottom = new Roo.bootstrap.Element({
29307 cls : 'roo-navigation-bar-text'
29310 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29311 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29313 var topText = new Roo.bootstrap.Element({
29315 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29318 var bottomText = new Roo.bootstrap.Element({
29320 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29323 topText.onRender(top.el, null);
29324 bottomText.onRender(bottom.el, null);
29327 item.bottomEl = bottom;
29330 this.barItems.push(item);
29335 getActive : function()
29337 var active = false;
29339 Roo.each(this.barItems, function(v){
29341 if (!v.isActive()) {
29353 setActiveItem : function(item)
29357 Roo.each(this.barItems, function(v){
29358 if (v.rid == item.rid) {
29362 if (v.isActive()) {
29363 v.setActive(false);
29368 item.setActive(true);
29370 this.fireEvent('changed', this, item, prev);
29373 getBarItem: function(rid)
29377 Roo.each(this.barItems, function(e) {
29378 if (e.rid != rid) {
29389 indexOfItem : function(item)
29393 Roo.each(this.barItems, function(v, i){
29395 if (v.rid != item.rid) {
29406 setActiveNext : function()
29408 var i = this.indexOfItem(this.getActive());
29410 if (i > this.barItems.length) {
29414 this.setActiveItem(this.barItems[i+1]);
29417 setActivePrev : function()
29419 var i = this.indexOfItem(this.getActive());
29425 this.setActiveItem(this.barItems[i-1]);
29428 format : function()
29430 if(!this.barItems.length){
29434 var width = 100 / this.barItems.length;
29436 Roo.each(this.barItems, function(i){
29437 i.el.setStyle('width', width + '%');
29438 i.topEl.el.setStyle('width', width + '%');
29439 i.bottomEl.el.setStyle('width', width + '%');
29448 * Nav Progress Item
29453 * @class Roo.bootstrap.NavProgressItem
29454 * @extends Roo.bootstrap.Component
29455 * Bootstrap NavProgressItem class
29456 * @cfg {String} rid the reference id
29457 * @cfg {Boolean} active (true|false) Is item active default false
29458 * @cfg {Boolean} disabled (true|false) Is item active default false
29459 * @cfg {String} html
29460 * @cfg {String} position (top|bottom) text position default bottom
29461 * @cfg {String} icon show icon instead of number
29464 * Create a new NavProgressItem
29465 * @param {Object} config The config object
29467 Roo.bootstrap.NavProgressItem = function(config){
29468 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29473 * The raw click event for the entire grid.
29474 * @param {Roo.bootstrap.NavProgressItem} this
29475 * @param {Roo.EventObject} e
29482 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29488 position : 'bottom',
29491 getAutoCreate : function()
29493 var iconCls = 'roo-navigation-bar-item-icon';
29495 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29499 cls: 'roo-navigation-bar-item',
29509 cfg.cls += ' active';
29512 cfg.cls += ' disabled';
29518 disable : function()
29520 this.setDisabled(true);
29523 enable : function()
29525 this.setDisabled(false);
29528 initEvents: function()
29530 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29532 this.iconEl.on('click', this.onClick, this);
29535 onClick : function(e)
29537 e.preventDefault();
29543 if(this.fireEvent('click', this, e) === false){
29547 this.parent().setActiveItem(this);
29550 isActive: function ()
29552 return this.active;
29555 setActive : function(state)
29557 if(this.active == state){
29561 this.active = state;
29564 this.el.addClass('active');
29568 this.el.removeClass('active');
29573 setDisabled : function(state)
29575 if(this.disabled == state){
29579 this.disabled = state;
29582 this.el.addClass('disabled');
29586 this.el.removeClass('disabled');
29589 tooltipEl : function()
29591 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29604 * @class Roo.bootstrap.FieldLabel
29605 * @extends Roo.bootstrap.Component
29606 * Bootstrap FieldLabel class
29607 * @cfg {String} html contents of the element
29608 * @cfg {String} tag tag of the element default label
29609 * @cfg {String} cls class of the element
29610 * @cfg {String} target label target
29611 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29612 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29613 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29614 * @cfg {String} iconTooltip default "This field is required"
29617 * Create a new FieldLabel
29618 * @param {Object} config The config object
29621 Roo.bootstrap.FieldLabel = function(config){
29622 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29627 * Fires after the field has been marked as invalid.
29628 * @param {Roo.form.FieldLabel} this
29629 * @param {String} msg The validation message
29634 * Fires after the field has been validated with no errors.
29635 * @param {Roo.form.FieldLabel} this
29641 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
29648 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29649 validClass : 'text-success fa fa-lg fa-check',
29650 iconTooltip : 'This field is required',
29652 getAutoCreate : function(){
29656 cls : 'roo-bootstrap-field-label ' + this.cls,
29662 tooltip : this.iconTooltip
29674 initEvents: function()
29676 Roo.bootstrap.Element.superclass.initEvents.call(this);
29678 this.iconEl = this.el.select('i', true).first();
29680 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29682 Roo.bootstrap.FieldLabel.register(this);
29686 * Mark this field as valid
29688 markValid : function()
29690 this.iconEl.show();
29692 this.iconEl.removeClass(this.invalidClass);
29694 this.iconEl.addClass(this.validClass);
29696 this.fireEvent('valid', this);
29700 * Mark this field as invalid
29701 * @param {String} msg The validation message
29703 markInvalid : function(msg)
29705 this.iconEl.show();
29707 this.iconEl.removeClass(this.validClass);
29709 this.iconEl.addClass(this.invalidClass);
29711 this.fireEvent('invalid', this, msg);
29717 Roo.apply(Roo.bootstrap.FieldLabel, {
29722 * register a FieldLabel Group
29723 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29725 register : function(label)
29727 if(this.groups.hasOwnProperty(label.target)){
29731 this.groups[label.target] = label;
29735 * fetch a FieldLabel Group based on the target
29736 * @param {string} target
29737 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29739 get: function(target) {
29740 if (typeof(this.groups[target]) == 'undefined') {
29744 return this.groups[target] ;
29753 * page DateSplitField.
29759 * @class Roo.bootstrap.DateSplitField
29760 * @extends Roo.bootstrap.Component
29761 * Bootstrap DateSplitField class
29762 * @cfg {string} fieldLabel - the label associated
29763 * @cfg {Number} labelWidth set the width of label (0-12)
29764 * @cfg {String} labelAlign (top|left)
29765 * @cfg {Boolean} dayAllowBlank (true|false) default false
29766 * @cfg {Boolean} monthAllowBlank (true|false) default false
29767 * @cfg {Boolean} yearAllowBlank (true|false) default false
29768 * @cfg {string} dayPlaceholder
29769 * @cfg {string} monthPlaceholder
29770 * @cfg {string} yearPlaceholder
29771 * @cfg {string} dayFormat default 'd'
29772 * @cfg {string} monthFormat default 'm'
29773 * @cfg {string} yearFormat default 'Y'
29774 * @cfg {Number} labellg set the width of label (1-12)
29775 * @cfg {Number} labelmd set the width of label (1-12)
29776 * @cfg {Number} labelsm set the width of label (1-12)
29777 * @cfg {Number} labelxs set the width of label (1-12)
29781 * Create a new DateSplitField
29782 * @param {Object} config The config object
29785 Roo.bootstrap.DateSplitField = function(config){
29786 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29792 * getting the data of years
29793 * @param {Roo.bootstrap.DateSplitField} this
29794 * @param {Object} years
29799 * getting the data of days
29800 * @param {Roo.bootstrap.DateSplitField} this
29801 * @param {Object} days
29806 * Fires after the field has been marked as invalid.
29807 * @param {Roo.form.Field} this
29808 * @param {String} msg The validation message
29813 * Fires after the field has been validated with no errors.
29814 * @param {Roo.form.Field} this
29820 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
29823 labelAlign : 'top',
29825 dayAllowBlank : false,
29826 monthAllowBlank : false,
29827 yearAllowBlank : false,
29828 dayPlaceholder : '',
29829 monthPlaceholder : '',
29830 yearPlaceholder : '',
29834 isFormField : true,
29840 getAutoCreate : function()
29844 cls : 'row roo-date-split-field-group',
29849 cls : 'form-hidden-field roo-date-split-field-group-value',
29855 var labelCls = 'col-md-12';
29856 var contentCls = 'col-md-4';
29858 if(this.fieldLabel){
29862 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29866 html : this.fieldLabel
29871 if(this.labelAlign == 'left'){
29873 if(this.labelWidth > 12){
29874 label.style = "width: " + this.labelWidth + 'px';
29877 if(this.labelWidth < 13 && this.labelmd == 0){
29878 this.labelmd = this.labelWidth;
29881 if(this.labellg > 0){
29882 labelCls = ' col-lg-' + this.labellg;
29883 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29886 if(this.labelmd > 0){
29887 labelCls = ' col-md-' + this.labelmd;
29888 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29891 if(this.labelsm > 0){
29892 labelCls = ' col-sm-' + this.labelsm;
29893 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29896 if(this.labelxs > 0){
29897 labelCls = ' col-xs-' + this.labelxs;
29898 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29902 label.cls += ' ' + labelCls;
29904 cfg.cn.push(label);
29907 Roo.each(['day', 'month', 'year'], function(t){
29910 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29917 inputEl: function ()
29919 return this.el.select('.roo-date-split-field-group-value', true).first();
29922 onRender : function(ct, position)
29926 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29928 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29930 this.dayField = new Roo.bootstrap.ComboBox({
29931 allowBlank : this.dayAllowBlank,
29932 alwaysQuery : true,
29933 displayField : 'value',
29936 forceSelection : true,
29938 placeholder : this.dayPlaceholder,
29939 selectOnFocus : true,
29940 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29941 triggerAction : 'all',
29943 valueField : 'value',
29944 store : new Roo.data.SimpleStore({
29945 data : (function() {
29947 _this.fireEvent('days', _this, days);
29950 fields : [ 'value' ]
29953 select : function (_self, record, index)
29955 _this.setValue(_this.getValue());
29960 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29962 this.monthField = new Roo.bootstrap.MonthField({
29963 after : '<i class=\"fa fa-calendar\"></i>',
29964 allowBlank : this.monthAllowBlank,
29965 placeholder : this.monthPlaceholder,
29968 render : function (_self)
29970 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29971 e.preventDefault();
29975 select : function (_self, oldvalue, newvalue)
29977 _this.setValue(_this.getValue());
29982 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29984 this.yearField = new Roo.bootstrap.ComboBox({
29985 allowBlank : this.yearAllowBlank,
29986 alwaysQuery : true,
29987 displayField : 'value',
29990 forceSelection : true,
29992 placeholder : this.yearPlaceholder,
29993 selectOnFocus : true,
29994 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29995 triggerAction : 'all',
29997 valueField : 'value',
29998 store : new Roo.data.SimpleStore({
29999 data : (function() {
30001 _this.fireEvent('years', _this, years);
30004 fields : [ 'value' ]
30007 select : function (_self, record, index)
30009 _this.setValue(_this.getValue());
30014 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30017 setValue : function(v, format)
30019 this.inputEl.dom.value = v;
30021 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30023 var d = Date.parseDate(v, f);
30030 this.setDay(d.format(this.dayFormat));
30031 this.setMonth(d.format(this.monthFormat));
30032 this.setYear(d.format(this.yearFormat));
30039 setDay : function(v)
30041 this.dayField.setValue(v);
30042 this.inputEl.dom.value = this.getValue();
30047 setMonth : function(v)
30049 this.monthField.setValue(v, true);
30050 this.inputEl.dom.value = this.getValue();
30055 setYear : function(v)
30057 this.yearField.setValue(v);
30058 this.inputEl.dom.value = this.getValue();
30063 getDay : function()
30065 return this.dayField.getValue();
30068 getMonth : function()
30070 return this.monthField.getValue();
30073 getYear : function()
30075 return this.yearField.getValue();
30078 getValue : function()
30080 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30082 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30092 this.inputEl.dom.value = '';
30097 validate : function()
30099 var d = this.dayField.validate();
30100 var m = this.monthField.validate();
30101 var y = this.yearField.validate();
30106 (!this.dayAllowBlank && !d) ||
30107 (!this.monthAllowBlank && !m) ||
30108 (!this.yearAllowBlank && !y)
30113 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30122 this.markInvalid();
30127 markValid : function()
30130 var label = this.el.select('label', true).first();
30131 var icon = this.el.select('i.fa-star', true).first();
30137 this.fireEvent('valid', this);
30141 * Mark this field as invalid
30142 * @param {String} msg The validation message
30144 markInvalid : function(msg)
30147 var label = this.el.select('label', true).first();
30148 var icon = this.el.select('i.fa-star', true).first();
30150 if(label && !icon){
30151 this.el.select('.roo-date-split-field-label', true).createChild({
30153 cls : 'text-danger fa fa-lg fa-star',
30154 tooltip : 'This field is required',
30155 style : 'margin-right:5px;'
30159 this.fireEvent('invalid', this, msg);
30162 clearInvalid : function()
30164 var label = this.el.select('label', true).first();
30165 var icon = this.el.select('i.fa-star', true).first();
30171 this.fireEvent('valid', this);
30174 getName: function()
30184 * http://masonry.desandro.com
30186 * The idea is to render all the bricks based on vertical width...
30188 * The original code extends 'outlayer' - we might need to use that....
30194 * @class Roo.bootstrap.LayoutMasonry
30195 * @extends Roo.bootstrap.Component
30196 * Bootstrap Layout Masonry class
30199 * Create a new Element
30200 * @param {Object} config The config object
30203 Roo.bootstrap.LayoutMasonry = function(config){
30205 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30209 Roo.bootstrap.LayoutMasonry.register(this);
30215 * Fire after layout the items
30216 * @param {Roo.bootstrap.LayoutMasonry} this
30217 * @param {Roo.EventObject} e
30224 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30227 * @cfg {Boolean} isLayoutInstant = no animation?
30229 isLayoutInstant : false, // needed?
30232 * @cfg {Number} boxWidth width of the columns
30237 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30242 * @cfg {Number} padWidth padding below box..
30247 * @cfg {Number} gutter gutter width..
30252 * @cfg {Number} maxCols maximum number of columns
30258 * @cfg {Boolean} isAutoInitial defalut true
30260 isAutoInitial : true,
30265 * @cfg {Boolean} isHorizontal defalut false
30267 isHorizontal : false,
30269 currentSize : null,
30275 bricks: null, //CompositeElement
30279 _isLayoutInited : false,
30281 // isAlternative : false, // only use for vertical layout...
30284 * @cfg {Number} alternativePadWidth padding below box..
30286 alternativePadWidth : 50,
30288 selectedBrick : [],
30290 getAutoCreate : function(){
30292 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30296 cls: 'blog-masonary-wrapper ' + this.cls,
30298 cls : 'mas-boxes masonary'
30305 getChildContainer: function( )
30307 if (this.boxesEl) {
30308 return this.boxesEl;
30311 this.boxesEl = this.el.select('.mas-boxes').first();
30313 return this.boxesEl;
30317 initEvents : function()
30321 if(this.isAutoInitial){
30322 Roo.log('hook children rendered');
30323 this.on('childrenrendered', function() {
30324 Roo.log('children rendered');
30330 initial : function()
30332 this.selectedBrick = [];
30334 this.currentSize = this.el.getBox(true);
30336 Roo.EventManager.onWindowResize(this.resize, this);
30338 if(!this.isAutoInitial){
30346 //this.layout.defer(500,this);
30350 resize : function()
30352 var cs = this.el.getBox(true);
30355 this.currentSize.width == cs.width &&
30356 this.currentSize.x == cs.x &&
30357 this.currentSize.height == cs.height &&
30358 this.currentSize.y == cs.y
30360 Roo.log("no change in with or X or Y");
30364 this.currentSize = cs;
30370 layout : function()
30372 this._resetLayout();
30374 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30376 this.layoutItems( isInstant );
30378 this._isLayoutInited = true;
30380 this.fireEvent('layout', this);
30384 _resetLayout : function()
30386 if(this.isHorizontal){
30387 this.horizontalMeasureColumns();
30391 this.verticalMeasureColumns();
30395 verticalMeasureColumns : function()
30397 this.getContainerWidth();
30399 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30400 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30404 var boxWidth = this.boxWidth + this.padWidth;
30406 if(this.containerWidth < this.boxWidth){
30407 boxWidth = this.containerWidth
30410 var containerWidth = this.containerWidth;
30412 var cols = Math.floor(containerWidth / boxWidth);
30414 this.cols = Math.max( cols, 1 );
30416 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30418 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30420 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30422 this.colWidth = boxWidth + avail - this.padWidth;
30424 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30425 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30428 horizontalMeasureColumns : function()
30430 this.getContainerWidth();
30432 var boxWidth = this.boxWidth;
30434 if(this.containerWidth < boxWidth){
30435 boxWidth = this.containerWidth;
30438 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30440 this.el.setHeight(boxWidth);
30444 getContainerWidth : function()
30446 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30449 layoutItems : function( isInstant )
30451 Roo.log(this.bricks);
30453 var items = Roo.apply([], this.bricks);
30455 if(this.isHorizontal){
30456 this._horizontalLayoutItems( items , isInstant );
30460 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30461 // this._verticalAlternativeLayoutItems( items , isInstant );
30465 this._verticalLayoutItems( items , isInstant );
30469 _verticalLayoutItems : function ( items , isInstant)
30471 if ( !items || !items.length ) {
30476 ['xs', 'xs', 'xs', 'tall'],
30477 ['xs', 'xs', 'tall'],
30478 ['xs', 'xs', 'sm'],
30479 ['xs', 'xs', 'xs'],
30485 ['sm', 'xs', 'xs'],
30489 ['tall', 'xs', 'xs', 'xs'],
30490 ['tall', 'xs', 'xs'],
30502 Roo.each(items, function(item, k){
30504 switch (item.size) {
30505 // these layouts take up a full box,
30516 boxes.push([item]);
30539 var filterPattern = function(box, length)
30547 var pattern = box.slice(0, length);
30551 Roo.each(pattern, function(i){
30552 format.push(i.size);
30555 Roo.each(standard, function(s){
30557 if(String(s) != String(format)){
30566 if(!match && length == 1){
30571 filterPattern(box, length - 1);
30575 queue.push(pattern);
30577 box = box.slice(length, box.length);
30579 filterPattern(box, 4);
30585 Roo.each(boxes, function(box, k){
30591 if(box.length == 1){
30596 filterPattern(box, 4);
30600 this._processVerticalLayoutQueue( queue, isInstant );
30604 // _verticalAlternativeLayoutItems : function( items , isInstant )
30606 // if ( !items || !items.length ) {
30610 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
30614 _horizontalLayoutItems : function ( items , isInstant)
30616 if ( !items || !items.length || items.length < 3) {
30622 var eItems = items.slice(0, 3);
30624 items = items.slice(3, items.length);
30627 ['xs', 'xs', 'xs', 'wide'],
30628 ['xs', 'xs', 'wide'],
30629 ['xs', 'xs', 'sm'],
30630 ['xs', 'xs', 'xs'],
30636 ['sm', 'xs', 'xs'],
30640 ['wide', 'xs', 'xs', 'xs'],
30641 ['wide', 'xs', 'xs'],
30654 Roo.each(items, function(item, k){
30656 switch (item.size) {
30667 boxes.push([item]);
30691 var filterPattern = function(box, length)
30699 var pattern = box.slice(0, length);
30703 Roo.each(pattern, function(i){
30704 format.push(i.size);
30707 Roo.each(standard, function(s){
30709 if(String(s) != String(format)){
30718 if(!match && length == 1){
30723 filterPattern(box, length - 1);
30727 queue.push(pattern);
30729 box = box.slice(length, box.length);
30731 filterPattern(box, 4);
30737 Roo.each(boxes, function(box, k){
30743 if(box.length == 1){
30748 filterPattern(box, 4);
30755 var pos = this.el.getBox(true);
30759 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30761 var hit_end = false;
30763 Roo.each(queue, function(box){
30767 Roo.each(box, function(b){
30769 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30779 Roo.each(box, function(b){
30781 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30784 mx = Math.max(mx, b.x);
30788 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30792 Roo.each(box, function(b){
30794 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30808 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30811 /** Sets position of item in DOM
30812 * @param {Element} item
30813 * @param {Number} x - horizontal position
30814 * @param {Number} y - vertical position
30815 * @param {Boolean} isInstant - disables transitions
30817 _processVerticalLayoutQueue : function( queue, isInstant )
30819 var pos = this.el.getBox(true);
30824 for (var i = 0; i < this.cols; i++){
30828 Roo.each(queue, function(box, k){
30830 var col = k % this.cols;
30832 Roo.each(box, function(b,kk){
30834 b.el.position('absolute');
30836 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30837 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30839 if(b.size == 'md-left' || b.size == 'md-right'){
30840 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30841 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30844 b.el.setWidth(width);
30845 b.el.setHeight(height);
30847 b.el.select('iframe',true).setSize(width,height);
30851 for (var i = 0; i < this.cols; i++){
30853 if(maxY[i] < maxY[col]){
30858 col = Math.min(col, i);
30862 x = pos.x + col * (this.colWidth + this.padWidth);
30866 var positions = [];
30868 switch (box.length){
30870 positions = this.getVerticalOneBoxColPositions(x, y, box);
30873 positions = this.getVerticalTwoBoxColPositions(x, y, box);
30876 positions = this.getVerticalThreeBoxColPositions(x, y, box);
30879 positions = this.getVerticalFourBoxColPositions(x, y, box);
30885 Roo.each(box, function(b,kk){
30887 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30889 var sz = b.el.getSize();
30891 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30899 for (var i = 0; i < this.cols; i++){
30900 mY = Math.max(mY, maxY[i]);
30903 this.el.setHeight(mY - pos.y);
30907 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30909 // var pos = this.el.getBox(true);
30912 // var maxX = pos.right;
30914 // var maxHeight = 0;
30916 // Roo.each(items, function(item, k){
30920 // item.el.position('absolute');
30922 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30924 // item.el.setWidth(width);
30926 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30928 // item.el.setHeight(height);
30931 // item.el.setXY([x, y], isInstant ? false : true);
30933 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30936 // y = y + height + this.alternativePadWidth;
30938 // maxHeight = maxHeight + height + this.alternativePadWidth;
30942 // this.el.setHeight(maxHeight);
30946 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30948 var pos = this.el.getBox(true);
30953 var maxX = pos.right;
30955 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30957 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30959 Roo.each(queue, function(box, k){
30961 Roo.each(box, function(b, kk){
30963 b.el.position('absolute');
30965 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30966 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30968 if(b.size == 'md-left' || b.size == 'md-right'){
30969 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30970 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30973 b.el.setWidth(width);
30974 b.el.setHeight(height);
30982 var positions = [];
30984 switch (box.length){
30986 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30989 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30992 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30995 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31001 Roo.each(box, function(b,kk){
31003 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31005 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31013 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31015 Roo.each(eItems, function(b,k){
31017 b.size = (k == 0) ? 'sm' : 'xs';
31018 b.x = (k == 0) ? 2 : 1;
31019 b.y = (k == 0) ? 2 : 1;
31021 b.el.position('absolute');
31023 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31025 b.el.setWidth(width);
31027 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31029 b.el.setHeight(height);
31033 var positions = [];
31036 x : maxX - this.unitWidth * 2 - this.gutter,
31041 x : maxX - this.unitWidth,
31042 y : minY + (this.unitWidth + this.gutter) * 2
31046 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31050 Roo.each(eItems, function(b,k){
31052 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31058 getVerticalOneBoxColPositions : function(x, y, box)
31062 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31064 if(box[0].size == 'md-left'){
31068 if(box[0].size == 'md-right'){
31073 x : x + (this.unitWidth + this.gutter) * rand,
31080 getVerticalTwoBoxColPositions : function(x, y, box)
31084 if(box[0].size == 'xs'){
31088 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31092 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31106 x : x + (this.unitWidth + this.gutter) * 2,
31107 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31114 getVerticalThreeBoxColPositions : function(x, y, box)
31118 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31126 x : x + (this.unitWidth + this.gutter) * 1,
31131 x : x + (this.unitWidth + this.gutter) * 2,
31139 if(box[0].size == 'xs' && box[1].size == 'xs'){
31148 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31152 x : x + (this.unitWidth + this.gutter) * 1,
31166 x : x + (this.unitWidth + this.gutter) * 2,
31171 x : x + (this.unitWidth + this.gutter) * 2,
31172 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31179 getVerticalFourBoxColPositions : function(x, y, box)
31183 if(box[0].size == 'xs'){
31192 y : y + (this.unitHeight + this.gutter) * 1
31197 y : y + (this.unitHeight + this.gutter) * 2
31201 x : x + (this.unitWidth + this.gutter) * 1,
31215 x : x + (this.unitWidth + this.gutter) * 2,
31220 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31221 y : y + (this.unitHeight + this.gutter) * 1
31225 x : x + (this.unitWidth + this.gutter) * 2,
31226 y : y + (this.unitWidth + this.gutter) * 2
31233 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31237 if(box[0].size == 'md-left'){
31239 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31246 if(box[0].size == 'md-right'){
31248 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31249 y : minY + (this.unitWidth + this.gutter) * 1
31255 var rand = Math.floor(Math.random() * (4 - box[0].y));
31258 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31259 y : minY + (this.unitWidth + this.gutter) * rand
31266 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31270 if(box[0].size == 'xs'){
31273 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31278 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31279 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31287 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31292 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31293 y : minY + (this.unitWidth + this.gutter) * 2
31300 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31304 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31307 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31312 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31313 y : minY + (this.unitWidth + this.gutter) * 1
31317 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31318 y : minY + (this.unitWidth + this.gutter) * 2
31325 if(box[0].size == 'xs' && box[1].size == 'xs'){
31328 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31333 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31338 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31339 y : minY + (this.unitWidth + this.gutter) * 1
31347 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31352 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31353 y : minY + (this.unitWidth + this.gutter) * 2
31357 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31358 y : minY + (this.unitWidth + this.gutter) * 2
31365 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31369 if(box[0].size == 'xs'){
31372 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31377 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31387 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].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
31411 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),
31412 y : minY + (this.unitWidth + this.gutter) * 2
31420 * remove a Masonry Brick
31421 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31423 removeBrick : function(brick_id)
31429 for (var i = 0; i<this.bricks.length; i++) {
31430 if (this.bricks[i].id == brick_id) {
31431 this.bricks.splice(i,1);
31432 this.el.dom.removeChild(Roo.get(brick_id).dom);
31439 * adds a Masonry Brick
31440 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31442 addBrick : function(cfg)
31444 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31445 //this.register(cn);
31446 cn.parentId = this.id;
31447 cn.onRender(this.el, null);
31452 * register a Masonry Brick
31453 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31456 register : function(brick)
31458 this.bricks.push(brick);
31459 brick.masonryId = this.id;
31463 * clear all the Masonry Brick
31465 clearAll : function()
31468 //this.getChildContainer().dom.innerHTML = "";
31469 this.el.dom.innerHTML = '';
31472 getSelected : function()
31474 if (!this.selectedBrick) {
31478 return this.selectedBrick;
31482 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31486 * register a Masonry Layout
31487 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31490 register : function(layout)
31492 this.groups[layout.id] = layout;
31495 * fetch a Masonry Layout based on the masonry layout ID
31496 * @param {string} the masonry layout to add
31497 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31500 get: function(layout_id) {
31501 if (typeof(this.groups[layout_id]) == 'undefined') {
31504 return this.groups[layout_id] ;
31516 * http://masonry.desandro.com
31518 * The idea is to render all the bricks based on vertical width...
31520 * The original code extends 'outlayer' - we might need to use that....
31526 * @class Roo.bootstrap.LayoutMasonryAuto
31527 * @extends Roo.bootstrap.Component
31528 * Bootstrap Layout Masonry class
31531 * Create a new Element
31532 * @param {Object} config The config object
31535 Roo.bootstrap.LayoutMasonryAuto = function(config){
31536 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31539 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31542 * @cfg {Boolean} isFitWidth - resize the width..
31544 isFitWidth : false, // options..
31546 * @cfg {Boolean} isOriginLeft = left align?
31548 isOriginLeft : true,
31550 * @cfg {Boolean} isOriginTop = top align?
31552 isOriginTop : false,
31554 * @cfg {Boolean} isLayoutInstant = no animation?
31556 isLayoutInstant : false, // needed?
31558 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31560 isResizingContainer : true,
31562 * @cfg {Number} columnWidth width of the columns
31568 * @cfg {Number} maxCols maximum number of columns
31573 * @cfg {Number} padHeight padding below box..
31579 * @cfg {Boolean} isAutoInitial defalut true
31582 isAutoInitial : true,
31588 initialColumnWidth : 0,
31589 currentSize : null,
31591 colYs : null, // array.
31598 bricks: null, //CompositeElement
31599 cols : 0, // array?
31600 // element : null, // wrapped now this.el
31601 _isLayoutInited : null,
31604 getAutoCreate : function(){
31608 cls: 'blog-masonary-wrapper ' + this.cls,
31610 cls : 'mas-boxes masonary'
31617 getChildContainer: function( )
31619 if (this.boxesEl) {
31620 return this.boxesEl;
31623 this.boxesEl = this.el.select('.mas-boxes').first();
31625 return this.boxesEl;
31629 initEvents : function()
31633 if(this.isAutoInitial){
31634 Roo.log('hook children rendered');
31635 this.on('childrenrendered', function() {
31636 Roo.log('children rendered');
31643 initial : function()
31645 this.reloadItems();
31647 this.currentSize = this.el.getBox(true);
31649 /// was window resize... - let's see if this works..
31650 Roo.EventManager.onWindowResize(this.resize, this);
31652 if(!this.isAutoInitial){
31657 this.layout.defer(500,this);
31660 reloadItems: function()
31662 this.bricks = this.el.select('.masonry-brick', true);
31664 this.bricks.each(function(b) {
31665 //Roo.log(b.getSize());
31666 if (!b.attr('originalwidth')) {
31667 b.attr('originalwidth', b.getSize().width);
31672 Roo.log(this.bricks.elements.length);
31675 resize : function()
31678 var cs = this.el.getBox(true);
31680 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31681 Roo.log("no change in with or X");
31684 this.currentSize = cs;
31688 layout : function()
31691 this._resetLayout();
31692 //this._manageStamps();
31694 // don't animate first layout
31695 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31696 this.layoutItems( isInstant );
31698 // flag for initalized
31699 this._isLayoutInited = true;
31702 layoutItems : function( isInstant )
31704 //var items = this._getItemsForLayout( this.items );
31705 // original code supports filtering layout items.. we just ignore it..
31707 this._layoutItems( this.bricks , isInstant );
31709 this._postLayout();
31711 _layoutItems : function ( items , isInstant)
31713 //this.fireEvent( 'layout', this, items );
31716 if ( !items || !items.elements.length ) {
31717 // no items, emit event with empty array
31722 items.each(function(item) {
31723 Roo.log("layout item");
31725 // get x/y object from method
31726 var position = this._getItemLayoutPosition( item );
31728 position.item = item;
31729 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31730 queue.push( position );
31733 this._processLayoutQueue( queue );
31735 /** Sets position of item in DOM
31736 * @param {Element} item
31737 * @param {Number} x - horizontal position
31738 * @param {Number} y - vertical position
31739 * @param {Boolean} isInstant - disables transitions
31741 _processLayoutQueue : function( queue )
31743 for ( var i=0, len = queue.length; i < len; i++ ) {
31744 var obj = queue[i];
31745 obj.item.position('absolute');
31746 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31752 * Any logic you want to do after each layout,
31753 * i.e. size the container
31755 _postLayout : function()
31757 this.resizeContainer();
31760 resizeContainer : function()
31762 if ( !this.isResizingContainer ) {
31765 var size = this._getContainerSize();
31767 this.el.setSize(size.width,size.height);
31768 this.boxesEl.setSize(size.width,size.height);
31774 _resetLayout : function()
31776 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31777 this.colWidth = this.el.getWidth();
31778 //this.gutter = this.el.getWidth();
31780 this.measureColumns();
31786 this.colYs.push( 0 );
31792 measureColumns : function()
31794 this.getContainerWidth();
31795 // if columnWidth is 0, default to outerWidth of first item
31796 if ( !this.columnWidth ) {
31797 var firstItem = this.bricks.first();
31798 Roo.log(firstItem);
31799 this.columnWidth = this.containerWidth;
31800 if (firstItem && firstItem.attr('originalwidth') ) {
31801 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31803 // columnWidth fall back to item of first element
31804 Roo.log("set column width?");
31805 this.initialColumnWidth = this.columnWidth ;
31807 // if first elem has no width, default to size of container
31812 if (this.initialColumnWidth) {
31813 this.columnWidth = this.initialColumnWidth;
31818 // column width is fixed at the top - however if container width get's smaller we should
31821 // this bit calcs how man columns..
31823 var columnWidth = this.columnWidth += this.gutter;
31825 // calculate columns
31826 var containerWidth = this.containerWidth + this.gutter;
31828 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31829 // fix rounding errors, typically with gutters
31830 var excess = columnWidth - containerWidth % columnWidth;
31833 // if overshoot is less than a pixel, round up, otherwise floor it
31834 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31835 cols = Math[ mathMethod ]( cols );
31836 this.cols = Math.max( cols, 1 );
31837 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31839 // padding positioning..
31840 var totalColWidth = this.cols * this.columnWidth;
31841 var padavail = this.containerWidth - totalColWidth;
31842 // so for 2 columns - we need 3 'pads'
31844 var padNeeded = (1+this.cols) * this.padWidth;
31846 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31848 this.columnWidth += padExtra
31849 //this.padWidth = Math.floor(padavail / ( this.cols));
31851 // adjust colum width so that padding is fixed??
31853 // we have 3 columns ... total = width * 3
31854 // we have X left over... that should be used by
31856 //if (this.expandC) {
31864 getContainerWidth : function()
31866 /* // container is parent if fit width
31867 var container = this.isFitWidth ? this.element.parentNode : this.element;
31868 // check that this.size and size are there
31869 // IE8 triggers resize on body size change, so they might not be
31871 var size = getSize( container ); //FIXME
31872 this.containerWidth = size && size.innerWidth; //FIXME
31875 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31879 _getItemLayoutPosition : function( item ) // what is item?
31881 // we resize the item to our columnWidth..
31883 item.setWidth(this.columnWidth);
31884 item.autoBoxAdjust = false;
31886 var sz = item.getSize();
31888 // how many columns does this brick span
31889 var remainder = this.containerWidth % this.columnWidth;
31891 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31892 // round if off by 1 pixel, otherwise use ceil
31893 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
31894 colSpan = Math.min( colSpan, this.cols );
31896 // normally this should be '1' as we dont' currently allow multi width columns..
31898 var colGroup = this._getColGroup( colSpan );
31899 // get the minimum Y value from the columns
31900 var minimumY = Math.min.apply( Math, colGroup );
31901 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31903 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
31905 // position the brick
31907 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31908 y: this.currentSize.y + minimumY + this.padHeight
31912 // apply setHeight to necessary columns
31913 var setHeight = minimumY + sz.height + this.padHeight;
31914 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
31916 var setSpan = this.cols + 1 - colGroup.length;
31917 for ( var i = 0; i < setSpan; i++ ) {
31918 this.colYs[ shortColIndex + i ] = setHeight ;
31925 * @param {Number} colSpan - number of columns the element spans
31926 * @returns {Array} colGroup
31928 _getColGroup : function( colSpan )
31930 if ( colSpan < 2 ) {
31931 // if brick spans only one column, use all the column Ys
31936 // how many different places could this brick fit horizontally
31937 var groupCount = this.cols + 1 - colSpan;
31938 // for each group potential horizontal position
31939 for ( var i = 0; i < groupCount; i++ ) {
31940 // make an array of colY values for that one group
31941 var groupColYs = this.colYs.slice( i, i + colSpan );
31942 // and get the max value of the array
31943 colGroup[i] = Math.max.apply( Math, groupColYs );
31948 _manageStamp : function( stamp )
31950 var stampSize = stamp.getSize();
31951 var offset = stamp.getBox();
31952 // get the columns that this stamp affects
31953 var firstX = this.isOriginLeft ? offset.x : offset.right;
31954 var lastX = firstX + stampSize.width;
31955 var firstCol = Math.floor( firstX / this.columnWidth );
31956 firstCol = Math.max( 0, firstCol );
31958 var lastCol = Math.floor( lastX / this.columnWidth );
31959 // lastCol should not go over if multiple of columnWidth #425
31960 lastCol -= lastX % this.columnWidth ? 0 : 1;
31961 lastCol = Math.min( this.cols - 1, lastCol );
31963 // set colYs to bottom of the stamp
31964 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31967 for ( var i = firstCol; i <= lastCol; i++ ) {
31968 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31973 _getContainerSize : function()
31975 this.maxY = Math.max.apply( Math, this.colYs );
31980 if ( this.isFitWidth ) {
31981 size.width = this._getContainerFitWidth();
31987 _getContainerFitWidth : function()
31989 var unusedCols = 0;
31990 // count unused columns
31993 if ( this.colYs[i] !== 0 ) {
31998 // fit container to columns that have been used
31999 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32002 needsResizeLayout : function()
32004 var previousWidth = this.containerWidth;
32005 this.getContainerWidth();
32006 return previousWidth !== this.containerWidth;
32021 * @class Roo.bootstrap.MasonryBrick
32022 * @extends Roo.bootstrap.Component
32023 * Bootstrap MasonryBrick class
32026 * Create a new MasonryBrick
32027 * @param {Object} config The config object
32030 Roo.bootstrap.MasonryBrick = function(config){
32032 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32034 Roo.bootstrap.MasonryBrick.register(this);
32040 * When a MasonryBrick is clcik
32041 * @param {Roo.bootstrap.MasonryBrick} this
32042 * @param {Roo.EventObject} e
32048 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32051 * @cfg {String} title
32055 * @cfg {String} html
32059 * @cfg {String} bgimage
32063 * @cfg {String} videourl
32067 * @cfg {String} cls
32071 * @cfg {String} href
32075 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32080 * @cfg {String} placetitle (center|bottom)
32085 * @cfg {Boolean} isFitContainer defalut true
32087 isFitContainer : true,
32090 * @cfg {Boolean} preventDefault defalut false
32092 preventDefault : false,
32095 * @cfg {Boolean} inverse defalut false
32097 maskInverse : false,
32099 getAutoCreate : function()
32101 if(!this.isFitContainer){
32102 return this.getSplitAutoCreate();
32105 var cls = 'masonry-brick masonry-brick-full';
32107 if(this.href.length){
32108 cls += ' masonry-brick-link';
32111 if(this.bgimage.length){
32112 cls += ' masonry-brick-image';
32115 if(this.maskInverse){
32116 cls += ' mask-inverse';
32119 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32120 cls += ' enable-mask';
32124 cls += ' masonry-' + this.size + '-brick';
32127 if(this.placetitle.length){
32129 switch (this.placetitle) {
32131 cls += ' masonry-center-title';
32134 cls += ' masonry-bottom-title';
32141 if(!this.html.length && !this.bgimage.length){
32142 cls += ' masonry-center-title';
32145 if(!this.html.length && this.bgimage.length){
32146 cls += ' masonry-bottom-title';
32151 cls += ' ' + this.cls;
32155 tag: (this.href.length) ? 'a' : 'div',
32160 cls: 'masonry-brick-mask'
32164 cls: 'masonry-brick-paragraph',
32170 if(this.href.length){
32171 cfg.href = this.href;
32174 var cn = cfg.cn[1].cn;
32176 if(this.title.length){
32179 cls: 'masonry-brick-title',
32184 if(this.html.length){
32187 cls: 'masonry-brick-text',
32192 if (!this.title.length && !this.html.length) {
32193 cfg.cn[1].cls += ' hide';
32196 if(this.bgimage.length){
32199 cls: 'masonry-brick-image-view',
32204 if(this.videourl.length){
32205 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32206 // youtube support only?
32209 cls: 'masonry-brick-image-view',
32212 allowfullscreen : true
32220 getSplitAutoCreate : function()
32222 var cls = 'masonry-brick masonry-brick-split';
32224 if(this.href.length){
32225 cls += ' masonry-brick-link';
32228 if(this.bgimage.length){
32229 cls += ' masonry-brick-image';
32233 cls += ' masonry-' + this.size + '-brick';
32236 switch (this.placetitle) {
32238 cls += ' masonry-center-title';
32241 cls += ' masonry-bottom-title';
32244 if(!this.bgimage.length){
32245 cls += ' masonry-center-title';
32248 if(this.bgimage.length){
32249 cls += ' masonry-bottom-title';
32255 cls += ' ' + this.cls;
32259 tag: (this.href.length) ? 'a' : 'div',
32264 cls: 'masonry-brick-split-head',
32268 cls: 'masonry-brick-paragraph',
32275 cls: 'masonry-brick-split-body',
32281 if(this.href.length){
32282 cfg.href = this.href;
32285 if(this.title.length){
32286 cfg.cn[0].cn[0].cn.push({
32288 cls: 'masonry-brick-title',
32293 if(this.html.length){
32294 cfg.cn[1].cn.push({
32296 cls: 'masonry-brick-text',
32301 if(this.bgimage.length){
32302 cfg.cn[0].cn.push({
32304 cls: 'masonry-brick-image-view',
32309 if(this.videourl.length){
32310 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32311 // youtube support only?
32312 cfg.cn[0].cn.cn.push({
32314 cls: 'masonry-brick-image-view',
32317 allowfullscreen : true
32324 initEvents: function()
32326 switch (this.size) {
32359 this.el.on('touchstart', this.onTouchStart, this);
32360 this.el.on('touchmove', this.onTouchMove, this);
32361 this.el.on('touchend', this.onTouchEnd, this);
32362 this.el.on('contextmenu', this.onContextMenu, this);
32364 this.el.on('mouseenter' ,this.enter, this);
32365 this.el.on('mouseleave', this.leave, this);
32366 this.el.on('click', this.onClick, this);
32369 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32370 this.parent().bricks.push(this);
32375 onClick: function(e, el)
32377 var time = this.endTimer - this.startTimer;
32378 // Roo.log(e.preventDefault());
32381 e.preventDefault();
32386 if(!this.preventDefault){
32390 e.preventDefault();
32392 if (this.activcClass != '') {
32393 this.selectBrick();
32396 this.fireEvent('click', this);
32399 enter: function(e, el)
32401 e.preventDefault();
32403 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32407 if(this.bgimage.length && this.html.length){
32408 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32412 leave: function(e, el)
32414 e.preventDefault();
32416 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32420 if(this.bgimage.length && this.html.length){
32421 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32425 onTouchStart: function(e, el)
32427 // e.preventDefault();
32429 this.touchmoved = false;
32431 if(!this.isFitContainer){
32435 if(!this.bgimage.length || !this.html.length){
32439 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32441 this.timer = new Date().getTime();
32445 onTouchMove: function(e, el)
32447 this.touchmoved = true;
32450 onContextMenu : function(e,el)
32452 e.preventDefault();
32453 e.stopPropagation();
32457 onTouchEnd: function(e, el)
32459 // e.preventDefault();
32461 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32468 if(!this.bgimage.length || !this.html.length){
32470 if(this.href.length){
32471 window.location.href = this.href;
32477 if(!this.isFitContainer){
32481 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32483 window.location.href = this.href;
32486 //selection on single brick only
32487 selectBrick : function() {
32489 if (!this.parentId) {
32493 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32494 var index = m.selectedBrick.indexOf(this.id);
32497 m.selectedBrick.splice(index,1);
32498 this.el.removeClass(this.activeClass);
32502 for(var i = 0; i < m.selectedBrick.length; i++) {
32503 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32504 b.el.removeClass(b.activeClass);
32507 m.selectedBrick = [];
32509 m.selectedBrick.push(this.id);
32510 this.el.addClass(this.activeClass);
32516 Roo.apply(Roo.bootstrap.MasonryBrick, {
32519 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32521 * register a Masonry Brick
32522 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32525 register : function(brick)
32527 //this.groups[brick.id] = brick;
32528 this.groups.add(brick.id, brick);
32531 * fetch a masonry brick based on the masonry brick ID
32532 * @param {string} the masonry brick to add
32533 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32536 get: function(brick_id)
32538 // if (typeof(this.groups[brick_id]) == 'undefined') {
32541 // return this.groups[brick_id] ;
32543 if(this.groups.key(brick_id)) {
32544 return this.groups.key(brick_id);
32562 * @class Roo.bootstrap.Brick
32563 * @extends Roo.bootstrap.Component
32564 * Bootstrap Brick class
32567 * Create a new Brick
32568 * @param {Object} config The config object
32571 Roo.bootstrap.Brick = function(config){
32572 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32578 * When a Brick is click
32579 * @param {Roo.bootstrap.Brick} this
32580 * @param {Roo.EventObject} e
32586 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32589 * @cfg {String} title
32593 * @cfg {String} html
32597 * @cfg {String} bgimage
32601 * @cfg {String} cls
32605 * @cfg {String} href
32609 * @cfg {String} video
32613 * @cfg {Boolean} square
32617 getAutoCreate : function()
32619 var cls = 'roo-brick';
32621 if(this.href.length){
32622 cls += ' roo-brick-link';
32625 if(this.bgimage.length){
32626 cls += ' roo-brick-image';
32629 if(!this.html.length && !this.bgimage.length){
32630 cls += ' roo-brick-center-title';
32633 if(!this.html.length && this.bgimage.length){
32634 cls += ' roo-brick-bottom-title';
32638 cls += ' ' + this.cls;
32642 tag: (this.href.length) ? 'a' : 'div',
32647 cls: 'roo-brick-paragraph',
32653 if(this.href.length){
32654 cfg.href = this.href;
32657 var cn = cfg.cn[0].cn;
32659 if(this.title.length){
32662 cls: 'roo-brick-title',
32667 if(this.html.length){
32670 cls: 'roo-brick-text',
32677 if(this.bgimage.length){
32680 cls: 'roo-brick-image-view',
32688 initEvents: function()
32690 if(this.title.length || this.html.length){
32691 this.el.on('mouseenter' ,this.enter, this);
32692 this.el.on('mouseleave', this.leave, this);
32695 Roo.EventManager.onWindowResize(this.resize, this);
32697 if(this.bgimage.length){
32698 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32699 this.imageEl.on('load', this.onImageLoad, this);
32706 onImageLoad : function()
32711 resize : function()
32713 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32715 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32717 if(this.bgimage.length){
32718 var image = this.el.select('.roo-brick-image-view', true).first();
32720 image.setWidth(paragraph.getWidth());
32723 image.setHeight(paragraph.getWidth());
32726 this.el.setHeight(image.getHeight());
32727 paragraph.setHeight(image.getHeight());
32733 enter: function(e, el)
32735 e.preventDefault();
32737 if(this.bgimage.length){
32738 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32739 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32743 leave: function(e, el)
32745 e.preventDefault();
32747 if(this.bgimage.length){
32748 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32749 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32765 * @class Roo.bootstrap.NumberField
32766 * @extends Roo.bootstrap.Input
32767 * Bootstrap NumberField class
32773 * Create a new NumberField
32774 * @param {Object} config The config object
32777 Roo.bootstrap.NumberField = function(config){
32778 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32781 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32784 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32786 allowDecimals : true,
32788 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32790 decimalSeparator : ".",
32792 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32794 decimalPrecision : 2,
32796 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32798 allowNegative : true,
32800 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32802 minValue : Number.NEGATIVE_INFINITY,
32804 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32806 maxValue : Number.MAX_VALUE,
32808 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32810 minText : "The minimum value for this field is {0}",
32812 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32814 maxText : "The maximum value for this field is {0}",
32816 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
32817 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32819 nanText : "{0} is not a valid number",
32821 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32826 initEvents : function()
32828 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32830 var allowed = "0123456789";
32832 if(this.allowDecimals){
32833 allowed += this.decimalSeparator;
32836 if(this.allowNegative){
32840 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32842 var keyPress = function(e){
32844 var k = e.getKey();
32846 var c = e.getCharCode();
32849 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32850 allowed.indexOf(String.fromCharCode(c)) === -1
32856 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32860 if(allowed.indexOf(String.fromCharCode(c)) === -1){
32865 this.el.on("keypress", keyPress, this);
32868 validateValue : function(value)
32871 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32875 var num = this.parseValue(value);
32878 this.markInvalid(String.format(this.nanText, value));
32882 if(num < this.minValue){
32883 this.markInvalid(String.format(this.minText, this.minValue));
32887 if(num > this.maxValue){
32888 this.markInvalid(String.format(this.maxText, this.maxValue));
32895 getValue : function()
32897 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32900 parseValue : function(value)
32902 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32903 return isNaN(value) ? '' : value;
32906 fixPrecision : function(value)
32908 var nan = isNaN(value);
32910 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32911 return nan ? '' : value;
32913 return parseFloat(value).toFixed(this.decimalPrecision);
32916 setValue : function(v)
32918 v = this.fixPrecision(v);
32919 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32922 decimalPrecisionFcn : function(v)
32924 return Math.floor(v);
32927 beforeBlur : function()
32933 var v = this.parseValue(this.getRawValue());
32948 * @class Roo.bootstrap.DocumentSlider
32949 * @extends Roo.bootstrap.Component
32950 * Bootstrap DocumentSlider class
32953 * Create a new DocumentViewer
32954 * @param {Object} config The config object
32957 Roo.bootstrap.DocumentSlider = function(config){
32958 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32965 * Fire after initEvent
32966 * @param {Roo.bootstrap.DocumentSlider} this
32971 * Fire after update
32972 * @param {Roo.bootstrap.DocumentSlider} this
32978 * @param {Roo.bootstrap.DocumentSlider} this
32984 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
32990 getAutoCreate : function()
32994 cls : 'roo-document-slider',
32998 cls : 'roo-document-slider-header',
33002 cls : 'roo-document-slider-header-title'
33008 cls : 'roo-document-slider-body',
33012 cls : 'roo-document-slider-prev',
33016 cls : 'fa fa-chevron-left'
33022 cls : 'roo-document-slider-thumb',
33026 cls : 'roo-document-slider-image'
33032 cls : 'roo-document-slider-next',
33036 cls : 'fa fa-chevron-right'
33048 initEvents : function()
33050 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33051 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33053 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33054 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33056 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33057 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33059 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33060 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33062 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33063 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33065 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33066 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33068 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33069 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33071 this.thumbEl.on('click', this.onClick, this);
33073 this.prevIndicator.on('click', this.prev, this);
33075 this.nextIndicator.on('click', this.next, this);
33079 initial : function()
33081 if(this.files.length){
33082 this.indicator = 1;
33086 this.fireEvent('initial', this);
33089 update : function()
33091 this.imageEl.attr('src', this.files[this.indicator - 1]);
33093 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33095 this.prevIndicator.show();
33097 if(this.indicator == 1){
33098 this.prevIndicator.hide();
33101 this.nextIndicator.show();
33103 if(this.indicator == this.files.length){
33104 this.nextIndicator.hide();
33107 this.thumbEl.scrollTo('top');
33109 this.fireEvent('update', this);
33112 onClick : function(e)
33114 e.preventDefault();
33116 this.fireEvent('click', this);
33121 e.preventDefault();
33123 this.indicator = Math.max(1, this.indicator - 1);
33130 e.preventDefault();
33132 this.indicator = Math.min(this.files.length, this.indicator + 1);
33146 * @class Roo.bootstrap.RadioSet
33147 * @extends Roo.bootstrap.Input
33148 * Bootstrap RadioSet class
33149 * @cfg {String} indicatorpos (left|right) default left
33150 * @cfg {Boolean} inline (true|false) inline the element (default true)
33151 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33153 * Create a new RadioSet
33154 * @param {Object} config The config object
33157 Roo.bootstrap.RadioSet = function(config){
33159 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33163 Roo.bootstrap.RadioSet.register(this);
33168 * Fires when the element is checked or unchecked.
33169 * @param {Roo.bootstrap.RadioSet} this This radio
33170 * @param {Roo.bootstrap.Radio} item The checked item
33177 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33185 indicatorpos : 'left',
33187 getAutoCreate : function()
33191 cls : 'roo-radio-set-label',
33195 html : this.fieldLabel
33200 if(this.indicatorpos == 'left'){
33203 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33204 tooltip : 'This field is required'
33209 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33210 tooltip : 'This field is required'
33216 cls : 'roo-radio-set-items'
33219 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33221 if (align === 'left' && this.fieldLabel.length) {
33224 cls : "roo-radio-set-right",
33230 if(this.labelWidth > 12){
33231 label.style = "width: " + this.labelWidth + 'px';
33234 if(this.labelWidth < 13 && this.labelmd == 0){
33235 this.labelmd = this.labelWidth;
33238 if(this.labellg > 0){
33239 label.cls += ' col-lg-' + this.labellg;
33240 items.cls += ' col-lg-' + (12 - this.labellg);
33243 if(this.labelmd > 0){
33244 label.cls += ' col-md-' + this.labelmd;
33245 items.cls += ' col-md-' + (12 - this.labelmd);
33248 if(this.labelsm > 0){
33249 label.cls += ' col-sm-' + this.labelsm;
33250 items.cls += ' col-sm-' + (12 - this.labelsm);
33253 if(this.labelxs > 0){
33254 label.cls += ' col-xs-' + this.labelxs;
33255 items.cls += ' col-xs-' + (12 - this.labelxs);
33261 cls : 'roo-radio-set',
33265 cls : 'roo-radio-set-input',
33268 value : this.value ? this.value : ''
33275 if(this.weight.length){
33276 cfg.cls += ' roo-radio-' + this.weight;
33280 cfg.cls += ' roo-radio-set-inline';
33287 initEvents : function()
33289 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33290 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33292 if(!this.fieldLabel.length){
33293 this.labelEl.hide();
33296 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33297 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33299 this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33300 this.indicatorEl().hide();
33302 this.originalValue = this.getValue();
33306 inputEl: function ()
33308 return this.el.select('.roo-radio-set-input', true).first();
33311 getChildContainer : function()
33313 return this.itemsEl;
33316 register : function(item)
33318 this.radioes.push(item);
33322 validate : function()
33326 Roo.each(this.radioes, function(i){
33335 if(this.allowBlank) {
33339 if(this.disabled || valid){
33344 this.markInvalid();
33349 markValid : function()
33351 if(this.labelEl.isVisible(true)){
33352 this.indicatorEl().hide();
33355 this.el.removeClass([this.invalidClass, this.validClass]);
33356 this.el.addClass(this.validClass);
33358 this.fireEvent('valid', this);
33361 markInvalid : function(msg)
33363 if(this.allowBlank || this.disabled){
33367 if(this.labelEl.isVisible(true)){
33368 this.indicatorEl().show();
33371 this.el.removeClass([this.invalidClass, this.validClass]);
33372 this.el.addClass(this.invalidClass);
33374 this.fireEvent('invalid', this, msg);
33378 setValue : function(v, suppressEvent)
33382 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33385 Roo.each(this.radioes, function(i){
33388 i.el.removeClass('checked');
33390 if(i.value === v || i.value.toString() === v.toString()){
33392 i.el.addClass('checked');
33394 if(suppressEvent !== true){
33395 this.fireEvent('check', this, i);
33404 clearInvalid : function(){
33406 if(!this.el || this.preventMark){
33410 this.el.removeClass([this.invalidClass]);
33412 this.fireEvent('valid', this);
33417 Roo.apply(Roo.bootstrap.RadioSet, {
33421 register : function(set)
33423 this.groups[set.name] = set;
33426 get: function(name)
33428 if (typeof(this.groups[name]) == 'undefined') {
33432 return this.groups[name] ;
33438 * Ext JS Library 1.1.1
33439 * Copyright(c) 2006-2007, Ext JS, LLC.
33441 * Originally Released Under LGPL - original licence link has changed is not relivant.
33444 * <script type="text/javascript">
33449 * @class Roo.bootstrap.SplitBar
33450 * @extends Roo.util.Observable
33451 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33455 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33456 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33457 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33458 split.minSize = 100;
33459 split.maxSize = 600;
33460 split.animate = true;
33461 split.on('moved', splitterMoved);
33464 * Create a new SplitBar
33465 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33466 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33467 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33468 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33469 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33470 position of the SplitBar).
33472 Roo.bootstrap.SplitBar = function(cfg){
33477 // dragElement : elm
33478 // resizingElement: el,
33480 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33481 // placement : Roo.bootstrap.SplitBar.LEFT ,
33482 // existingProxy ???
33485 this.el = Roo.get(cfg.dragElement, true);
33486 this.el.dom.unselectable = "on";
33488 this.resizingEl = Roo.get(cfg.resizingElement, true);
33492 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33493 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33496 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33499 * The minimum size of the resizing element. (Defaults to 0)
33505 * The maximum size of the resizing element. (Defaults to 2000)
33508 this.maxSize = 2000;
33511 * Whether to animate the transition to the new size
33514 this.animate = false;
33517 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33520 this.useShim = false;
33525 if(!cfg.existingProxy){
33527 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33529 this.proxy = Roo.get(cfg.existingProxy).dom;
33532 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33535 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33538 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33541 this.dragSpecs = {};
33544 * @private The adapter to use to positon and resize elements
33546 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33547 this.adapter.init(this);
33549 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33551 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33552 this.el.addClass("roo-splitbar-h");
33555 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33556 this.el.addClass("roo-splitbar-v");
33562 * Fires when the splitter is moved (alias for {@link #event-moved})
33563 * @param {Roo.bootstrap.SplitBar} this
33564 * @param {Number} newSize the new width or height
33569 * Fires when the splitter is moved
33570 * @param {Roo.bootstrap.SplitBar} this
33571 * @param {Number} newSize the new width or height
33575 * @event beforeresize
33576 * Fires before the splitter is dragged
33577 * @param {Roo.bootstrap.SplitBar} this
33579 "beforeresize" : true,
33581 "beforeapply" : true
33584 Roo.util.Observable.call(this);
33587 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33588 onStartProxyDrag : function(x, y){
33589 this.fireEvent("beforeresize", this);
33591 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
33593 o.enableDisplayMode("block");
33594 // all splitbars share the same overlay
33595 Roo.bootstrap.SplitBar.prototype.overlay = o;
33597 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33598 this.overlay.show();
33599 Roo.get(this.proxy).setDisplayed("block");
33600 var size = this.adapter.getElementSize(this);
33601 this.activeMinSize = this.getMinimumSize();;
33602 this.activeMaxSize = this.getMaximumSize();;
33603 var c1 = size - this.activeMinSize;
33604 var c2 = Math.max(this.activeMaxSize - size, 0);
33605 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33606 this.dd.resetConstraints();
33607 this.dd.setXConstraint(
33608 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
33609 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33611 this.dd.setYConstraint(0, 0);
33613 this.dd.resetConstraints();
33614 this.dd.setXConstraint(0, 0);
33615 this.dd.setYConstraint(
33616 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
33617 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33620 this.dragSpecs.startSize = size;
33621 this.dragSpecs.startPoint = [x, y];
33622 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33626 * @private Called after the drag operation by the DDProxy
33628 onEndProxyDrag : function(e){
33629 Roo.get(this.proxy).setDisplayed(false);
33630 var endPoint = Roo.lib.Event.getXY(e);
33632 this.overlay.hide();
33635 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33636 newSize = this.dragSpecs.startSize +
33637 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33638 endPoint[0] - this.dragSpecs.startPoint[0] :
33639 this.dragSpecs.startPoint[0] - endPoint[0]
33642 newSize = this.dragSpecs.startSize +
33643 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33644 endPoint[1] - this.dragSpecs.startPoint[1] :
33645 this.dragSpecs.startPoint[1] - endPoint[1]
33648 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33649 if(newSize != this.dragSpecs.startSize){
33650 if(this.fireEvent('beforeapply', this, newSize) !== false){
33651 this.adapter.setElementSize(this, newSize);
33652 this.fireEvent("moved", this, newSize);
33653 this.fireEvent("resize", this, newSize);
33659 * Get the adapter this SplitBar uses
33660 * @return The adapter object
33662 getAdapter : function(){
33663 return this.adapter;
33667 * Set the adapter this SplitBar uses
33668 * @param {Object} adapter A SplitBar adapter object
33670 setAdapter : function(adapter){
33671 this.adapter = adapter;
33672 this.adapter.init(this);
33676 * Gets the minimum size for the resizing element
33677 * @return {Number} The minimum size
33679 getMinimumSize : function(){
33680 return this.minSize;
33684 * Sets the minimum size for the resizing element
33685 * @param {Number} minSize The minimum size
33687 setMinimumSize : function(minSize){
33688 this.minSize = minSize;
33692 * Gets the maximum size for the resizing element
33693 * @return {Number} The maximum size
33695 getMaximumSize : function(){
33696 return this.maxSize;
33700 * Sets the maximum size for the resizing element
33701 * @param {Number} maxSize The maximum size
33703 setMaximumSize : function(maxSize){
33704 this.maxSize = maxSize;
33708 * Sets the initialize size for the resizing element
33709 * @param {Number} size The initial size
33711 setCurrentSize : function(size){
33712 var oldAnimate = this.animate;
33713 this.animate = false;
33714 this.adapter.setElementSize(this, size);
33715 this.animate = oldAnimate;
33719 * Destroy this splitbar.
33720 * @param {Boolean} removeEl True to remove the element
33722 destroy : function(removeEl){
33724 this.shim.remove();
33727 this.proxy.parentNode.removeChild(this.proxy);
33735 * @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.
33737 Roo.bootstrap.SplitBar.createProxy = function(dir){
33738 var proxy = new Roo.Element(document.createElement("div"));
33739 proxy.unselectable();
33740 var cls = 'roo-splitbar-proxy';
33741 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33742 document.body.appendChild(proxy.dom);
33747 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33748 * Default Adapter. It assumes the splitter and resizing element are not positioned
33749 * elements and only gets/sets the width of the element. Generally used for table based layouts.
33751 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33754 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33755 // do nothing for now
33756 init : function(s){
33760 * Called before drag operations to get the current size of the resizing element.
33761 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33763 getElementSize : function(s){
33764 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33765 return s.resizingEl.getWidth();
33767 return s.resizingEl.getHeight();
33772 * Called after drag operations to set the size of the resizing element.
33773 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33774 * @param {Number} newSize The new size to set
33775 * @param {Function} onComplete A function to be invoked when resizing is complete
33777 setElementSize : function(s, newSize, onComplete){
33778 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33780 s.resizingEl.setWidth(newSize);
33782 onComplete(s, newSize);
33785 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33790 s.resizingEl.setHeight(newSize);
33792 onComplete(s, newSize);
33795 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33802 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33803 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33804 * Adapter that moves the splitter element to align with the resized sizing element.
33805 * Used with an absolute positioned SplitBar.
33806 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33807 * document.body, make sure you assign an id to the body element.
33809 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33810 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33811 this.container = Roo.get(container);
33814 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33815 init : function(s){
33816 this.basic.init(s);
33819 getElementSize : function(s){
33820 return this.basic.getElementSize(s);
33823 setElementSize : function(s, newSize, onComplete){
33824 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33827 moveSplitter : function(s){
33828 var yes = Roo.bootstrap.SplitBar;
33829 switch(s.placement){
33831 s.el.setX(s.resizingEl.getRight());
33834 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33837 s.el.setY(s.resizingEl.getBottom());
33840 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33847 * Orientation constant - Create a vertical SplitBar
33851 Roo.bootstrap.SplitBar.VERTICAL = 1;
33854 * Orientation constant - Create a horizontal SplitBar
33858 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33861 * Placement constant - The resizing element is to the left of the splitter element
33865 Roo.bootstrap.SplitBar.LEFT = 1;
33868 * Placement constant - The resizing element is to the right of the splitter element
33872 Roo.bootstrap.SplitBar.RIGHT = 2;
33875 * Placement constant - The resizing element is positioned above the splitter element
33879 Roo.bootstrap.SplitBar.TOP = 3;
33882 * Placement constant - The resizing element is positioned under splitter element
33886 Roo.bootstrap.SplitBar.BOTTOM = 4;
33887 Roo.namespace("Roo.bootstrap.layout");/*
33889 * Ext JS Library 1.1.1
33890 * Copyright(c) 2006-2007, Ext JS, LLC.
33892 * Originally Released Under LGPL - original licence link has changed is not relivant.
33895 * <script type="text/javascript">
33899 * @class Roo.bootstrap.layout.Manager
33900 * @extends Roo.bootstrap.Component
33901 * Base class for layout managers.
33903 Roo.bootstrap.layout.Manager = function(config)
33905 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33911 /** false to disable window resize monitoring @type Boolean */
33912 this.monitorWindowResize = true;
33917 * Fires when a layout is performed.
33918 * @param {Roo.LayoutManager} this
33922 * @event regionresized
33923 * Fires when the user resizes a region.
33924 * @param {Roo.LayoutRegion} region The resized region
33925 * @param {Number} newSize The new size (width for east/west, height for north/south)
33927 "regionresized" : true,
33929 * @event regioncollapsed
33930 * Fires when a region is collapsed.
33931 * @param {Roo.LayoutRegion} region The collapsed region
33933 "regioncollapsed" : true,
33935 * @event regionexpanded
33936 * Fires when a region is expanded.
33937 * @param {Roo.LayoutRegion} region The expanded region
33939 "regionexpanded" : true
33941 this.updating = false;
33944 this.el = Roo.get(config.el);
33950 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33955 monitorWindowResize : true,
33961 onRender : function(ct, position)
33964 this.el = Roo.get(ct);
33967 //this.fireEvent('render',this);
33971 initEvents: function()
33975 // ie scrollbar fix
33976 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33977 document.body.scroll = "no";
33978 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33979 this.el.position('relative');
33981 this.id = this.el.id;
33982 this.el.addClass("roo-layout-container");
33983 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33984 if(this.el.dom != document.body ) {
33985 this.el.on('resize', this.layout,this);
33986 this.el.on('show', this.layout,this);
33992 * Returns true if this layout is currently being updated
33993 * @return {Boolean}
33995 isUpdating : function(){
33996 return this.updating;
34000 * Suspend the LayoutManager from doing auto-layouts while
34001 * making multiple add or remove calls
34003 beginUpdate : function(){
34004 this.updating = true;
34008 * Restore auto-layouts and optionally disable the manager from performing a layout
34009 * @param {Boolean} noLayout true to disable a layout update
34011 endUpdate : function(noLayout){
34012 this.updating = false;
34018 layout: function(){
34022 onRegionResized : function(region, newSize){
34023 this.fireEvent("regionresized", region, newSize);
34027 onRegionCollapsed : function(region){
34028 this.fireEvent("regioncollapsed", region);
34031 onRegionExpanded : function(region){
34032 this.fireEvent("regionexpanded", region);
34036 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34037 * performs box-model adjustments.
34038 * @return {Object} The size as an object {width: (the width), height: (the height)}
34040 getViewSize : function()
34043 if(this.el.dom != document.body){
34044 size = this.el.getSize();
34046 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34048 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34049 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34054 * Returns the Element this layout is bound to.
34055 * @return {Roo.Element}
34057 getEl : function(){
34062 * Returns the specified region.
34063 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34064 * @return {Roo.LayoutRegion}
34066 getRegion : function(target){
34067 return this.regions[target.toLowerCase()];
34070 onWindowResize : function(){
34071 if(this.monitorWindowResize){
34078 * Ext JS Library 1.1.1
34079 * Copyright(c) 2006-2007, Ext JS, LLC.
34081 * Originally Released Under LGPL - original licence link has changed is not relivant.
34084 * <script type="text/javascript">
34087 * @class Roo.bootstrap.layout.Border
34088 * @extends Roo.bootstrap.layout.Manager
34089 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34090 * please see: examples/bootstrap/nested.html<br><br>
34092 <b>The container the layout is rendered into can be either the body element or any other element.
34093 If it is not the body element, the container needs to either be an absolute positioned element,
34094 or you will need to add "position:relative" to the css of the container. You will also need to specify
34095 the container size if it is not the body element.</b>
34098 * Create a new Border
34099 * @param {Object} config Configuration options
34101 Roo.bootstrap.layout.Border = function(config){
34102 config = config || {};
34103 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34107 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34108 if(config[region]){
34109 config[region].region = region;
34110 this.addRegion(config[region]);
34116 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34118 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34120 * Creates and adds a new region if it doesn't already exist.
34121 * @param {String} target The target region key (north, south, east, west or center).
34122 * @param {Object} config The regions config object
34123 * @return {BorderLayoutRegion} The new region
34125 addRegion : function(config)
34127 if(!this.regions[config.region]){
34128 var r = this.factory(config);
34129 this.bindRegion(r);
34131 return this.regions[config.region];
34135 bindRegion : function(r){
34136 this.regions[r.config.region] = r;
34138 r.on("visibilitychange", this.layout, this);
34139 r.on("paneladded", this.layout, this);
34140 r.on("panelremoved", this.layout, this);
34141 r.on("invalidated", this.layout, this);
34142 r.on("resized", this.onRegionResized, this);
34143 r.on("collapsed", this.onRegionCollapsed, this);
34144 r.on("expanded", this.onRegionExpanded, this);
34148 * Performs a layout update.
34150 layout : function()
34152 if(this.updating) {
34156 // render all the rebions if they have not been done alreayd?
34157 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34158 if(this.regions[region] && !this.regions[region].bodyEl){
34159 this.regions[region].onRender(this.el)
34163 var size = this.getViewSize();
34164 var w = size.width;
34165 var h = size.height;
34170 //var x = 0, y = 0;
34172 var rs = this.regions;
34173 var north = rs["north"];
34174 var south = rs["south"];
34175 var west = rs["west"];
34176 var east = rs["east"];
34177 var center = rs["center"];
34178 //if(this.hideOnLayout){ // not supported anymore
34179 //c.el.setStyle("display", "none");
34181 if(north && north.isVisible()){
34182 var b = north.getBox();
34183 var m = north.getMargins();
34184 b.width = w - (m.left+m.right);
34187 centerY = b.height + b.y + m.bottom;
34188 centerH -= centerY;
34189 north.updateBox(this.safeBox(b));
34191 if(south && south.isVisible()){
34192 var b = south.getBox();
34193 var m = south.getMargins();
34194 b.width = w - (m.left+m.right);
34196 var totalHeight = (b.height + m.top + m.bottom);
34197 b.y = h - totalHeight + m.top;
34198 centerH -= totalHeight;
34199 south.updateBox(this.safeBox(b));
34201 if(west && west.isVisible()){
34202 var b = west.getBox();
34203 var m = west.getMargins();
34204 b.height = centerH - (m.top+m.bottom);
34206 b.y = centerY + m.top;
34207 var totalWidth = (b.width + m.left + m.right);
34208 centerX += totalWidth;
34209 centerW -= totalWidth;
34210 west.updateBox(this.safeBox(b));
34212 if(east && east.isVisible()){
34213 var b = east.getBox();
34214 var m = east.getMargins();
34215 b.height = centerH - (m.top+m.bottom);
34216 var totalWidth = (b.width + m.left + m.right);
34217 b.x = w - totalWidth + m.left;
34218 b.y = centerY + m.top;
34219 centerW -= totalWidth;
34220 east.updateBox(this.safeBox(b));
34223 var m = center.getMargins();
34225 x: centerX + m.left,
34226 y: centerY + m.top,
34227 width: centerW - (m.left+m.right),
34228 height: centerH - (m.top+m.bottom)
34230 //if(this.hideOnLayout){
34231 //center.el.setStyle("display", "block");
34233 center.updateBox(this.safeBox(centerBox));
34236 this.fireEvent("layout", this);
34240 safeBox : function(box){
34241 box.width = Math.max(0, box.width);
34242 box.height = Math.max(0, box.height);
34247 * Adds a ContentPanel (or subclass) to this layout.
34248 * @param {String} target The target region key (north, south, east, west or center).
34249 * @param {Roo.ContentPanel} panel The panel to add
34250 * @return {Roo.ContentPanel} The added panel
34252 add : function(target, panel){
34254 target = target.toLowerCase();
34255 return this.regions[target].add(panel);
34259 * Remove a ContentPanel (or subclass) to this layout.
34260 * @param {String} target The target region key (north, south, east, west or center).
34261 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34262 * @return {Roo.ContentPanel} The removed panel
34264 remove : function(target, panel){
34265 target = target.toLowerCase();
34266 return this.regions[target].remove(panel);
34270 * Searches all regions for a panel with the specified id
34271 * @param {String} panelId
34272 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34274 findPanel : function(panelId){
34275 var rs = this.regions;
34276 for(var target in rs){
34277 if(typeof rs[target] != "function"){
34278 var p = rs[target].getPanel(panelId);
34288 * Searches all regions for a panel with the specified id and activates (shows) it.
34289 * @param {String/ContentPanel} panelId The panels id or the panel itself
34290 * @return {Roo.ContentPanel} The shown panel or null
34292 showPanel : function(panelId) {
34293 var rs = this.regions;
34294 for(var target in rs){
34295 var r = rs[target];
34296 if(typeof r != "function"){
34297 if(r.hasPanel(panelId)){
34298 return r.showPanel(panelId);
34306 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34307 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34310 restoreState : function(provider){
34312 provider = Roo.state.Manager;
34314 var sm = new Roo.LayoutStateManager();
34315 sm.init(this, provider);
34321 * Adds a xtype elements to the layout.
34325 xtype : 'ContentPanel',
34332 xtype : 'NestedLayoutPanel',
34338 items : [ ... list of content panels or nested layout panels.. ]
34342 * @param {Object} cfg Xtype definition of item to add.
34344 addxtype : function(cfg)
34346 // basically accepts a pannel...
34347 // can accept a layout region..!?!?
34348 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34351 // theory? children can only be panels??
34353 //if (!cfg.xtype.match(/Panel$/)) {
34358 if (typeof(cfg.region) == 'undefined') {
34359 Roo.log("Failed to add Panel, region was not set");
34363 var region = cfg.region;
34369 xitems = cfg.items;
34376 case 'Content': // ContentPanel (el, cfg)
34377 case 'Scroll': // ContentPanel (el, cfg)
34379 cfg.autoCreate = true;
34380 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34382 // var el = this.el.createChild();
34383 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34386 this.add(region, ret);
34390 case 'TreePanel': // our new panel!
34391 cfg.el = this.el.createChild();
34392 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34393 this.add(region, ret);
34398 // create a new Layout (which is a Border Layout...
34400 var clayout = cfg.layout;
34401 clayout.el = this.el.createChild();
34402 clayout.items = clayout.items || [];
34406 // replace this exitems with the clayout ones..
34407 xitems = clayout.items;
34409 // force background off if it's in center...
34410 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34411 cfg.background = false;
34413 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34416 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34417 //console.log('adding nested layout panel ' + cfg.toSource());
34418 this.add(region, ret);
34419 nb = {}; /// find first...
34424 // needs grid and region
34426 //var el = this.getRegion(region).el.createChild();
34428 *var el = this.el.createChild();
34429 // create the grid first...
34430 cfg.grid.container = el;
34431 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34434 if (region == 'center' && this.active ) {
34435 cfg.background = false;
34438 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34440 this.add(region, ret);
34442 if (cfg.background) {
34443 // render grid on panel activation (if panel background)
34444 ret.on('activate', function(gp) {
34445 if (!gp.grid.rendered) {
34446 // gp.grid.render(el);
34450 // cfg.grid.render(el);
34456 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34457 // it was the old xcomponent building that caused this before.
34458 // espeically if border is the top element in the tree.
34468 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34470 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34471 this.add(region, ret);
34475 throw "Can not add '" + cfg.xtype + "' to Border";
34481 this.beginUpdate();
34485 Roo.each(xitems, function(i) {
34486 region = nb && i.region ? i.region : false;
34488 var add = ret.addxtype(i);
34491 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34492 if (!i.background) {
34493 abn[region] = nb[region] ;
34500 // make the last non-background panel active..
34501 //if (nb) { Roo.log(abn); }
34504 for(var r in abn) {
34505 region = this.getRegion(r);
34507 // tried using nb[r], but it does not work..
34509 region.showPanel(abn[r]);
34520 factory : function(cfg)
34523 var validRegions = Roo.bootstrap.layout.Border.regions;
34525 var target = cfg.region;
34528 var r = Roo.bootstrap.layout;
34532 return new r.North(cfg);
34534 return new r.South(cfg);
34536 return new r.East(cfg);
34538 return new r.West(cfg);
34540 return new r.Center(cfg);
34542 throw 'Layout region "'+target+'" not supported.';
34549 * Ext JS Library 1.1.1
34550 * Copyright(c) 2006-2007, Ext JS, LLC.
34552 * Originally Released Under LGPL - original licence link has changed is not relivant.
34555 * <script type="text/javascript">
34559 * @class Roo.bootstrap.layout.Basic
34560 * @extends Roo.util.Observable
34561 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34562 * and does not have a titlebar, tabs or any other features. All it does is size and position
34563 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34564 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34565 * @cfg {string} region the region that it inhabits..
34566 * @cfg {bool} skipConfig skip config?
34570 Roo.bootstrap.layout.Basic = function(config){
34572 this.mgr = config.mgr;
34574 this.position = config.region;
34576 var skipConfig = config.skipConfig;
34580 * @scope Roo.BasicLayoutRegion
34584 * @event beforeremove
34585 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34586 * @param {Roo.LayoutRegion} this
34587 * @param {Roo.ContentPanel} panel The panel
34588 * @param {Object} e The cancel event object
34590 "beforeremove" : true,
34592 * @event invalidated
34593 * Fires when the layout for this region is changed.
34594 * @param {Roo.LayoutRegion} this
34596 "invalidated" : true,
34598 * @event visibilitychange
34599 * Fires when this region is shown or hidden
34600 * @param {Roo.LayoutRegion} this
34601 * @param {Boolean} visibility true or false
34603 "visibilitychange" : true,
34605 * @event paneladded
34606 * Fires when a panel is added.
34607 * @param {Roo.LayoutRegion} this
34608 * @param {Roo.ContentPanel} panel The panel
34610 "paneladded" : true,
34612 * @event panelremoved
34613 * Fires when a panel is removed.
34614 * @param {Roo.LayoutRegion} this
34615 * @param {Roo.ContentPanel} panel The panel
34617 "panelremoved" : true,
34619 * @event beforecollapse
34620 * Fires when this region before collapse.
34621 * @param {Roo.LayoutRegion} this
34623 "beforecollapse" : true,
34626 * Fires when this region is collapsed.
34627 * @param {Roo.LayoutRegion} this
34629 "collapsed" : true,
34632 * Fires when this region is expanded.
34633 * @param {Roo.LayoutRegion} this
34638 * Fires when this region is slid into view.
34639 * @param {Roo.LayoutRegion} this
34641 "slideshow" : true,
34644 * Fires when this region slides out of view.
34645 * @param {Roo.LayoutRegion} this
34647 "slidehide" : true,
34649 * @event panelactivated
34650 * Fires when a panel is activated.
34651 * @param {Roo.LayoutRegion} this
34652 * @param {Roo.ContentPanel} panel The activated panel
34654 "panelactivated" : true,
34657 * Fires when the user resizes this region.
34658 * @param {Roo.LayoutRegion} this
34659 * @param {Number} newSize The new size (width for east/west, height for north/south)
34663 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34664 this.panels = new Roo.util.MixedCollection();
34665 this.panels.getKey = this.getPanelId.createDelegate(this);
34667 this.activePanel = null;
34668 // ensure listeners are added...
34670 if (config.listeners || config.events) {
34671 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34672 listeners : config.listeners || {},
34673 events : config.events || {}
34677 if(skipConfig !== true){
34678 this.applyConfig(config);
34682 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34684 getPanelId : function(p){
34688 applyConfig : function(config){
34689 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34690 this.config = config;
34695 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34696 * the width, for horizontal (north, south) the height.
34697 * @param {Number} newSize The new width or height
34699 resizeTo : function(newSize){
34700 var el = this.el ? this.el :
34701 (this.activePanel ? this.activePanel.getEl() : null);
34703 switch(this.position){
34706 el.setWidth(newSize);
34707 this.fireEvent("resized", this, newSize);
34711 el.setHeight(newSize);
34712 this.fireEvent("resized", this, newSize);
34718 getBox : function(){
34719 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34722 getMargins : function(){
34723 return this.margins;
34726 updateBox : function(box){
34728 var el = this.activePanel.getEl();
34729 el.dom.style.left = box.x + "px";
34730 el.dom.style.top = box.y + "px";
34731 this.activePanel.setSize(box.width, box.height);
34735 * Returns the container element for this region.
34736 * @return {Roo.Element}
34738 getEl : function(){
34739 return this.activePanel;
34743 * Returns true if this region is currently visible.
34744 * @return {Boolean}
34746 isVisible : function(){
34747 return this.activePanel ? true : false;
34750 setActivePanel : function(panel){
34751 panel = this.getPanel(panel);
34752 if(this.activePanel && this.activePanel != panel){
34753 this.activePanel.setActiveState(false);
34754 this.activePanel.getEl().setLeftTop(-10000,-10000);
34756 this.activePanel = panel;
34757 panel.setActiveState(true);
34759 panel.setSize(this.box.width, this.box.height);
34761 this.fireEvent("panelactivated", this, panel);
34762 this.fireEvent("invalidated");
34766 * Show the specified panel.
34767 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34768 * @return {Roo.ContentPanel} The shown panel or null
34770 showPanel : function(panel){
34771 panel = this.getPanel(panel);
34773 this.setActivePanel(panel);
34779 * Get the active panel for this region.
34780 * @return {Roo.ContentPanel} The active panel or null
34782 getActivePanel : function(){
34783 return this.activePanel;
34787 * Add the passed ContentPanel(s)
34788 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34789 * @return {Roo.ContentPanel} The panel added (if only one was added)
34791 add : function(panel){
34792 if(arguments.length > 1){
34793 for(var i = 0, len = arguments.length; i < len; i++) {
34794 this.add(arguments[i]);
34798 if(this.hasPanel(panel)){
34799 this.showPanel(panel);
34802 var el = panel.getEl();
34803 if(el.dom.parentNode != this.mgr.el.dom){
34804 this.mgr.el.dom.appendChild(el.dom);
34806 if(panel.setRegion){
34807 panel.setRegion(this);
34809 this.panels.add(panel);
34810 el.setStyle("position", "absolute");
34811 if(!panel.background){
34812 this.setActivePanel(panel);
34813 if(this.config.initialSize && this.panels.getCount()==1){
34814 this.resizeTo(this.config.initialSize);
34817 this.fireEvent("paneladded", this, panel);
34822 * Returns true if the panel is in this region.
34823 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34824 * @return {Boolean}
34826 hasPanel : function(panel){
34827 if(typeof panel == "object"){ // must be panel obj
34828 panel = panel.getId();
34830 return this.getPanel(panel) ? true : false;
34834 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34835 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34836 * @param {Boolean} preservePanel Overrides the config preservePanel option
34837 * @return {Roo.ContentPanel} The panel that was removed
34839 remove : function(panel, preservePanel){
34840 panel = this.getPanel(panel);
34845 this.fireEvent("beforeremove", this, panel, e);
34846 if(e.cancel === true){
34849 var panelId = panel.getId();
34850 this.panels.removeKey(panelId);
34855 * Returns the panel specified or null if it's not in this region.
34856 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34857 * @return {Roo.ContentPanel}
34859 getPanel : function(id){
34860 if(typeof id == "object"){ // must be panel obj
34863 return this.panels.get(id);
34867 * Returns this regions position (north/south/east/west/center).
34870 getPosition: function(){
34871 return this.position;
34875 * Ext JS Library 1.1.1
34876 * Copyright(c) 2006-2007, Ext JS, LLC.
34878 * Originally Released Under LGPL - original licence link has changed is not relivant.
34881 * <script type="text/javascript">
34885 * @class Roo.bootstrap.layout.Region
34886 * @extends Roo.bootstrap.layout.Basic
34887 * This class represents a region in a layout manager.
34889 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34890 * @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})
34891 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
34892 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
34893 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
34894 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
34895 * @cfg {String} title The title for the region (overrides panel titles)
34896 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
34897 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34898 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
34899 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34900 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
34901 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34902 * the space available, similar to FireFox 1.5 tabs (defaults to false)
34903 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
34904 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
34905 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
34907 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
34908 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
34909 * @cfg {Boolean} disableTabTips True to disable tab tooltips
34910 * @cfg {Number} width For East/West panels
34911 * @cfg {Number} height For North/South panels
34912 * @cfg {Boolean} split To show the splitter
34913 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
34915 * @cfg {string} cls Extra CSS classes to add to region
34917 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34918 * @cfg {string} region the region that it inhabits..
34921 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
34922 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
34924 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
34925 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
34926 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
34928 Roo.bootstrap.layout.Region = function(config)
34930 this.applyConfig(config);
34932 var mgr = config.mgr;
34933 var pos = config.region;
34934 config.skipConfig = true;
34935 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34938 this.onRender(mgr.el);
34941 this.visible = true;
34942 this.collapsed = false;
34943 this.unrendered_panels = [];
34946 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34948 position: '', // set by wrapper (eg. north/south etc..)
34949 unrendered_panels : null, // unrendered panels.
34950 createBody : function(){
34951 /** This region's body element
34952 * @type Roo.Element */
34953 this.bodyEl = this.el.createChild({
34955 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34959 onRender: function(ctr, pos)
34961 var dh = Roo.DomHelper;
34962 /** This region's container element
34963 * @type Roo.Element */
34964 this.el = dh.append(ctr.dom, {
34966 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34968 /** This region's title element
34969 * @type Roo.Element */
34971 this.titleEl = dh.append(this.el.dom,
34974 unselectable: "on",
34975 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34977 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
34978 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34981 this.titleEl.enableDisplayMode();
34982 /** This region's title text element
34983 * @type HTMLElement */
34984 this.titleTextEl = this.titleEl.dom.firstChild;
34985 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34987 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34988 this.closeBtn.enableDisplayMode();
34989 this.closeBtn.on("click", this.closeClicked, this);
34990 this.closeBtn.hide();
34992 this.createBody(this.config);
34993 if(this.config.hideWhenEmpty){
34995 this.on("paneladded", this.validateVisibility, this);
34996 this.on("panelremoved", this.validateVisibility, this);
34998 if(this.autoScroll){
34999 this.bodyEl.setStyle("overflow", "auto");
35001 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35003 //if(c.titlebar !== false){
35004 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35005 this.titleEl.hide();
35007 this.titleEl.show();
35008 if(this.config.title){
35009 this.titleTextEl.innerHTML = this.config.title;
35013 if(this.config.collapsed){
35014 this.collapse(true);
35016 if(this.config.hidden){
35020 if (this.unrendered_panels && this.unrendered_panels.length) {
35021 for (var i =0;i< this.unrendered_panels.length; i++) {
35022 this.add(this.unrendered_panels[i]);
35024 this.unrendered_panels = null;
35030 applyConfig : function(c)
35033 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35034 var dh = Roo.DomHelper;
35035 if(c.titlebar !== false){
35036 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35037 this.collapseBtn.on("click", this.collapse, this);
35038 this.collapseBtn.enableDisplayMode();
35040 if(c.showPin === true || this.showPin){
35041 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35042 this.stickBtn.enableDisplayMode();
35043 this.stickBtn.on("click", this.expand, this);
35044 this.stickBtn.hide();
35049 /** This region's collapsed element
35050 * @type Roo.Element */
35053 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35054 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35057 if(c.floatable !== false){
35058 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35059 this.collapsedEl.on("click", this.collapseClick, this);
35062 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35063 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35064 id: "message", unselectable: "on", style:{"float":"left"}});
35065 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35067 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35068 this.expandBtn.on("click", this.expand, this);
35072 if(this.collapseBtn){
35073 this.collapseBtn.setVisible(c.collapsible == true);
35076 this.cmargins = c.cmargins || this.cmargins ||
35077 (this.position == "west" || this.position == "east" ?
35078 {top: 0, left: 2, right:2, bottom: 0} :
35079 {top: 2, left: 0, right:0, bottom: 2});
35081 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35084 this.bottomTabs = c.tabPosition != "top";
35086 this.autoScroll = c.autoScroll || false;
35091 this.duration = c.duration || .30;
35092 this.slideDuration = c.slideDuration || .45;
35097 * Returns true if this region is currently visible.
35098 * @return {Boolean}
35100 isVisible : function(){
35101 return this.visible;
35105 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35106 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35108 //setCollapsedTitle : function(title){
35109 // title = title || " ";
35110 // if(this.collapsedTitleTextEl){
35111 // this.collapsedTitleTextEl.innerHTML = title;
35115 getBox : function(){
35117 // if(!this.collapsed){
35118 b = this.el.getBox(false, true);
35120 // b = this.collapsedEl.getBox(false, true);
35125 getMargins : function(){
35126 return this.margins;
35127 //return this.collapsed ? this.cmargins : this.margins;
35130 highlight : function(){
35131 this.el.addClass("x-layout-panel-dragover");
35134 unhighlight : function(){
35135 this.el.removeClass("x-layout-panel-dragover");
35138 updateBox : function(box)
35140 if (!this.bodyEl) {
35141 return; // not rendered yet..
35145 if(!this.collapsed){
35146 this.el.dom.style.left = box.x + "px";
35147 this.el.dom.style.top = box.y + "px";
35148 this.updateBody(box.width, box.height);
35150 this.collapsedEl.dom.style.left = box.x + "px";
35151 this.collapsedEl.dom.style.top = box.y + "px";
35152 this.collapsedEl.setSize(box.width, box.height);
35155 this.tabs.autoSizeTabs();
35159 updateBody : function(w, h)
35162 this.el.setWidth(w);
35163 w -= this.el.getBorderWidth("rl");
35164 if(this.config.adjustments){
35165 w += this.config.adjustments[0];
35168 if(h !== null && h > 0){
35169 this.el.setHeight(h);
35170 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35171 h -= this.el.getBorderWidth("tb");
35172 if(this.config.adjustments){
35173 h += this.config.adjustments[1];
35175 this.bodyEl.setHeight(h);
35177 h = this.tabs.syncHeight(h);
35180 if(this.panelSize){
35181 w = w !== null ? w : this.panelSize.width;
35182 h = h !== null ? h : this.panelSize.height;
35184 if(this.activePanel){
35185 var el = this.activePanel.getEl();
35186 w = w !== null ? w : el.getWidth();
35187 h = h !== null ? h : el.getHeight();
35188 this.panelSize = {width: w, height: h};
35189 this.activePanel.setSize(w, h);
35191 if(Roo.isIE && this.tabs){
35192 this.tabs.el.repaint();
35197 * Returns the container element for this region.
35198 * @return {Roo.Element}
35200 getEl : function(){
35205 * Hides this region.
35208 //if(!this.collapsed){
35209 this.el.dom.style.left = "-2000px";
35212 // this.collapsedEl.dom.style.left = "-2000px";
35213 // this.collapsedEl.hide();
35215 this.visible = false;
35216 this.fireEvent("visibilitychange", this, false);
35220 * Shows this region if it was previously hidden.
35223 //if(!this.collapsed){
35226 // this.collapsedEl.show();
35228 this.visible = true;
35229 this.fireEvent("visibilitychange", this, true);
35232 closeClicked : function(){
35233 if(this.activePanel){
35234 this.remove(this.activePanel);
35238 collapseClick : function(e){
35240 e.stopPropagation();
35243 e.stopPropagation();
35249 * Collapses this region.
35250 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35253 collapse : function(skipAnim, skipCheck = false){
35254 if(this.collapsed) {
35258 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35260 this.collapsed = true;
35262 this.split.el.hide();
35264 if(this.config.animate && skipAnim !== true){
35265 this.fireEvent("invalidated", this);
35266 this.animateCollapse();
35268 this.el.setLocation(-20000,-20000);
35270 this.collapsedEl.show();
35271 this.fireEvent("collapsed", this);
35272 this.fireEvent("invalidated", this);
35278 animateCollapse : function(){
35283 * Expands this region if it was previously collapsed.
35284 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35285 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35288 expand : function(e, skipAnim){
35290 e.stopPropagation();
35292 if(!this.collapsed || this.el.hasActiveFx()) {
35296 this.afterSlideIn();
35299 this.collapsed = false;
35300 if(this.config.animate && skipAnim !== true){
35301 this.animateExpand();
35305 this.split.el.show();
35307 this.collapsedEl.setLocation(-2000,-2000);
35308 this.collapsedEl.hide();
35309 this.fireEvent("invalidated", this);
35310 this.fireEvent("expanded", this);
35314 animateExpand : function(){
35318 initTabs : function()
35320 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35322 var ts = new Roo.bootstrap.panel.Tabs({
35323 el: this.bodyEl.dom,
35324 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35325 disableTooltips: this.config.disableTabTips,
35326 toolbar : this.config.toolbar
35329 if(this.config.hideTabs){
35330 ts.stripWrap.setDisplayed(false);
35333 ts.resizeTabs = this.config.resizeTabs === true;
35334 ts.minTabWidth = this.config.minTabWidth || 40;
35335 ts.maxTabWidth = this.config.maxTabWidth || 250;
35336 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35337 ts.monitorResize = false;
35338 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35339 ts.bodyEl.addClass('roo-layout-tabs-body');
35340 this.panels.each(this.initPanelAsTab, this);
35343 initPanelAsTab : function(panel){
35344 var ti = this.tabs.addTab(
35348 this.config.closeOnTab && panel.isClosable(),
35351 if(panel.tabTip !== undefined){
35352 ti.setTooltip(panel.tabTip);
35354 ti.on("activate", function(){
35355 this.setActivePanel(panel);
35358 if(this.config.closeOnTab){
35359 ti.on("beforeclose", function(t, e){
35361 this.remove(panel);
35365 panel.tabItem = ti;
35370 updatePanelTitle : function(panel, title)
35372 if(this.activePanel == panel){
35373 this.updateTitle(title);
35376 var ti = this.tabs.getTab(panel.getEl().id);
35378 if(panel.tabTip !== undefined){
35379 ti.setTooltip(panel.tabTip);
35384 updateTitle : function(title){
35385 if(this.titleTextEl && !this.config.title){
35386 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35390 setActivePanel : function(panel)
35392 panel = this.getPanel(panel);
35393 if(this.activePanel && this.activePanel != panel){
35394 this.activePanel.setActiveState(false);
35396 this.activePanel = panel;
35397 panel.setActiveState(true);
35398 if(this.panelSize){
35399 panel.setSize(this.panelSize.width, this.panelSize.height);
35402 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35404 this.updateTitle(panel.getTitle());
35406 this.fireEvent("invalidated", this);
35408 this.fireEvent("panelactivated", this, panel);
35412 * Shows the specified panel.
35413 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35414 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35416 showPanel : function(panel)
35418 panel = this.getPanel(panel);
35421 var tab = this.tabs.getTab(panel.getEl().id);
35422 if(tab.isHidden()){
35423 this.tabs.unhideTab(tab.id);
35427 this.setActivePanel(panel);
35434 * Get the active panel for this region.
35435 * @return {Roo.ContentPanel} The active panel or null
35437 getActivePanel : function(){
35438 return this.activePanel;
35441 validateVisibility : function(){
35442 if(this.panels.getCount() < 1){
35443 this.updateTitle(" ");
35444 this.closeBtn.hide();
35447 if(!this.isVisible()){
35454 * Adds the passed ContentPanel(s) to this region.
35455 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35456 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35458 add : function(panel)
35460 if(arguments.length > 1){
35461 for(var i = 0, len = arguments.length; i < len; i++) {
35462 this.add(arguments[i]);
35467 // if we have not been rendered yet, then we can not really do much of this..
35468 if (!this.bodyEl) {
35469 this.unrendered_panels.push(panel);
35476 if(this.hasPanel(panel)){
35477 this.showPanel(panel);
35480 panel.setRegion(this);
35481 this.panels.add(panel);
35482 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35483 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35484 // and hide them... ???
35485 this.bodyEl.dom.appendChild(panel.getEl().dom);
35486 if(panel.background !== true){
35487 this.setActivePanel(panel);
35489 this.fireEvent("paneladded", this, panel);
35496 this.initPanelAsTab(panel);
35500 if(panel.background !== true){
35501 this.tabs.activate(panel.getEl().id);
35503 this.fireEvent("paneladded", this, panel);
35508 * Hides the tab for the specified panel.
35509 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35511 hidePanel : function(panel){
35512 if(this.tabs && (panel = this.getPanel(panel))){
35513 this.tabs.hideTab(panel.getEl().id);
35518 * Unhides the tab for a previously hidden panel.
35519 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35521 unhidePanel : function(panel){
35522 if(this.tabs && (panel = this.getPanel(panel))){
35523 this.tabs.unhideTab(panel.getEl().id);
35527 clearPanels : function(){
35528 while(this.panels.getCount() > 0){
35529 this.remove(this.panels.first());
35534 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35535 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35536 * @param {Boolean} preservePanel Overrides the config preservePanel option
35537 * @return {Roo.ContentPanel} The panel that was removed
35539 remove : function(panel, preservePanel)
35541 panel = this.getPanel(panel);
35546 this.fireEvent("beforeremove", this, panel, e);
35547 if(e.cancel === true){
35550 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35551 var panelId = panel.getId();
35552 this.panels.removeKey(panelId);
35554 document.body.appendChild(panel.getEl().dom);
35557 this.tabs.removeTab(panel.getEl().id);
35558 }else if (!preservePanel){
35559 this.bodyEl.dom.removeChild(panel.getEl().dom);
35561 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35562 var p = this.panels.first();
35563 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35564 tempEl.appendChild(p.getEl().dom);
35565 this.bodyEl.update("");
35566 this.bodyEl.dom.appendChild(p.getEl().dom);
35568 this.updateTitle(p.getTitle());
35570 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35571 this.setActivePanel(p);
35573 panel.setRegion(null);
35574 if(this.activePanel == panel){
35575 this.activePanel = null;
35577 if(this.config.autoDestroy !== false && preservePanel !== true){
35578 try{panel.destroy();}catch(e){}
35580 this.fireEvent("panelremoved", this, panel);
35585 * Returns the TabPanel component used by this region
35586 * @return {Roo.TabPanel}
35588 getTabs : function(){
35592 createTool : function(parentEl, className){
35593 var btn = Roo.DomHelper.append(parentEl, {
35595 cls: "x-layout-tools-button",
35598 cls: "roo-layout-tools-button-inner " + className,
35602 btn.addClassOnOver("roo-layout-tools-button-over");
35607 * Ext JS Library 1.1.1
35608 * Copyright(c) 2006-2007, Ext JS, LLC.
35610 * Originally Released Under LGPL - original licence link has changed is not relivant.
35613 * <script type="text/javascript">
35619 * @class Roo.SplitLayoutRegion
35620 * @extends Roo.LayoutRegion
35621 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35623 Roo.bootstrap.layout.Split = function(config){
35624 this.cursor = config.cursor;
35625 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35628 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35630 splitTip : "Drag to resize.",
35631 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35632 useSplitTips : false,
35634 applyConfig : function(config){
35635 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35638 onRender : function(ctr,pos) {
35640 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35641 if(!this.config.split){
35646 var splitEl = Roo.DomHelper.append(ctr.dom, {
35648 id: this.el.id + "-split",
35649 cls: "roo-layout-split roo-layout-split-"+this.position,
35652 /** The SplitBar for this region
35653 * @type Roo.SplitBar */
35654 // does not exist yet...
35655 Roo.log([this.position, this.orientation]);
35657 this.split = new Roo.bootstrap.SplitBar({
35658 dragElement : splitEl,
35659 resizingElement: this.el,
35660 orientation : this.orientation
35663 this.split.on("moved", this.onSplitMove, this);
35664 this.split.useShim = this.config.useShim === true;
35665 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35666 if(this.useSplitTips){
35667 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35669 //if(config.collapsible){
35670 // this.split.el.on("dblclick", this.collapse, this);
35673 if(typeof this.config.minSize != "undefined"){
35674 this.split.minSize = this.config.minSize;
35676 if(typeof this.config.maxSize != "undefined"){
35677 this.split.maxSize = this.config.maxSize;
35679 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35680 this.hideSplitter();
35685 getHMaxSize : function(){
35686 var cmax = this.config.maxSize || 10000;
35687 var center = this.mgr.getRegion("center");
35688 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35691 getVMaxSize : function(){
35692 var cmax = this.config.maxSize || 10000;
35693 var center = this.mgr.getRegion("center");
35694 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35697 onSplitMove : function(split, newSize){
35698 this.fireEvent("resized", this, newSize);
35702 * Returns the {@link Roo.SplitBar} for this region.
35703 * @return {Roo.SplitBar}
35705 getSplitBar : function(){
35710 this.hideSplitter();
35711 Roo.bootstrap.layout.Split.superclass.hide.call(this);
35714 hideSplitter : function(){
35716 this.split.el.setLocation(-2000,-2000);
35717 this.split.el.hide();
35723 this.split.el.show();
35725 Roo.bootstrap.layout.Split.superclass.show.call(this);
35728 beforeSlide: function(){
35729 if(Roo.isGecko){// firefox overflow auto bug workaround
35730 this.bodyEl.clip();
35732 this.tabs.bodyEl.clip();
35734 if(this.activePanel){
35735 this.activePanel.getEl().clip();
35737 if(this.activePanel.beforeSlide){
35738 this.activePanel.beforeSlide();
35744 afterSlide : function(){
35745 if(Roo.isGecko){// firefox overflow auto bug workaround
35746 this.bodyEl.unclip();
35748 this.tabs.bodyEl.unclip();
35750 if(this.activePanel){
35751 this.activePanel.getEl().unclip();
35752 if(this.activePanel.afterSlide){
35753 this.activePanel.afterSlide();
35759 initAutoHide : function(){
35760 if(this.autoHide !== false){
35761 if(!this.autoHideHd){
35762 var st = new Roo.util.DelayedTask(this.slideIn, this);
35763 this.autoHideHd = {
35764 "mouseout": function(e){
35765 if(!e.within(this.el, true)){
35769 "mouseover" : function(e){
35775 this.el.on(this.autoHideHd);
35779 clearAutoHide : function(){
35780 if(this.autoHide !== false){
35781 this.el.un("mouseout", this.autoHideHd.mouseout);
35782 this.el.un("mouseover", this.autoHideHd.mouseover);
35786 clearMonitor : function(){
35787 Roo.get(document).un("click", this.slideInIf, this);
35790 // these names are backwards but not changed for compat
35791 slideOut : function(){
35792 if(this.isSlid || this.el.hasActiveFx()){
35795 this.isSlid = true;
35796 if(this.collapseBtn){
35797 this.collapseBtn.hide();
35799 this.closeBtnState = this.closeBtn.getStyle('display');
35800 this.closeBtn.hide();
35802 this.stickBtn.show();
35805 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35806 this.beforeSlide();
35807 this.el.setStyle("z-index", 10001);
35808 this.el.slideIn(this.getSlideAnchor(), {
35809 callback: function(){
35811 this.initAutoHide();
35812 Roo.get(document).on("click", this.slideInIf, this);
35813 this.fireEvent("slideshow", this);
35820 afterSlideIn : function(){
35821 this.clearAutoHide();
35822 this.isSlid = false;
35823 this.clearMonitor();
35824 this.el.setStyle("z-index", "");
35825 if(this.collapseBtn){
35826 this.collapseBtn.show();
35828 this.closeBtn.setStyle('display', this.closeBtnState);
35830 this.stickBtn.hide();
35832 this.fireEvent("slidehide", this);
35835 slideIn : function(cb){
35836 if(!this.isSlid || this.el.hasActiveFx()){
35840 this.isSlid = false;
35841 this.beforeSlide();
35842 this.el.slideOut(this.getSlideAnchor(), {
35843 callback: function(){
35844 this.el.setLeftTop(-10000, -10000);
35846 this.afterSlideIn();
35854 slideInIf : function(e){
35855 if(!e.within(this.el)){
35860 animateCollapse : function(){
35861 this.beforeSlide();
35862 this.el.setStyle("z-index", 20000);
35863 var anchor = this.getSlideAnchor();
35864 this.el.slideOut(anchor, {
35865 callback : function(){
35866 this.el.setStyle("z-index", "");
35867 this.collapsedEl.slideIn(anchor, {duration:.3});
35869 this.el.setLocation(-10000,-10000);
35871 this.fireEvent("collapsed", this);
35878 animateExpand : function(){
35879 this.beforeSlide();
35880 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35881 this.el.setStyle("z-index", 20000);
35882 this.collapsedEl.hide({
35885 this.el.slideIn(this.getSlideAnchor(), {
35886 callback : function(){
35887 this.el.setStyle("z-index", "");
35890 this.split.el.show();
35892 this.fireEvent("invalidated", this);
35893 this.fireEvent("expanded", this);
35921 getAnchor : function(){
35922 return this.anchors[this.position];
35925 getCollapseAnchor : function(){
35926 return this.canchors[this.position];
35929 getSlideAnchor : function(){
35930 return this.sanchors[this.position];
35933 getAlignAdj : function(){
35934 var cm = this.cmargins;
35935 switch(this.position){
35951 getExpandAdj : function(){
35952 var c = this.collapsedEl, cm = this.cmargins;
35953 switch(this.position){
35955 return [-(cm.right+c.getWidth()+cm.left), 0];
35958 return [cm.right+c.getWidth()+cm.left, 0];
35961 return [0, -(cm.top+cm.bottom+c.getHeight())];
35964 return [0, cm.top+cm.bottom+c.getHeight()];
35970 * Ext JS Library 1.1.1
35971 * Copyright(c) 2006-2007, Ext JS, LLC.
35973 * Originally Released Under LGPL - original licence link has changed is not relivant.
35976 * <script type="text/javascript">
35979 * These classes are private internal classes
35981 Roo.bootstrap.layout.Center = function(config){
35982 config.region = "center";
35983 Roo.bootstrap.layout.Region.call(this, config);
35984 this.visible = true;
35985 this.minWidth = config.minWidth || 20;
35986 this.minHeight = config.minHeight || 20;
35989 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35991 // center panel can't be hidden
35995 // center panel can't be hidden
35998 getMinWidth: function(){
35999 return this.minWidth;
36002 getMinHeight: function(){
36003 return this.minHeight;
36016 Roo.bootstrap.layout.North = function(config)
36018 config.region = 'north';
36019 config.cursor = 'n-resize';
36021 Roo.bootstrap.layout.Split.call(this, config);
36025 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36026 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36027 this.split.el.addClass("roo-layout-split-v");
36029 var size = config.initialSize || config.height;
36030 if(typeof size != "undefined"){
36031 this.el.setHeight(size);
36034 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36036 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36040 getBox : function(){
36041 if(this.collapsed){
36042 return this.collapsedEl.getBox();
36044 var box = this.el.getBox();
36046 box.height += this.split.el.getHeight();
36051 updateBox : function(box){
36052 if(this.split && !this.collapsed){
36053 box.height -= this.split.el.getHeight();
36054 this.split.el.setLeft(box.x);
36055 this.split.el.setTop(box.y+box.height);
36056 this.split.el.setWidth(box.width);
36058 if(this.collapsed){
36059 this.updateBody(box.width, null);
36061 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36069 Roo.bootstrap.layout.South = function(config){
36070 config.region = 'south';
36071 config.cursor = 's-resize';
36072 Roo.bootstrap.layout.Split.call(this, config);
36074 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36075 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36076 this.split.el.addClass("roo-layout-split-v");
36078 var size = config.initialSize || config.height;
36079 if(typeof size != "undefined"){
36080 this.el.setHeight(size);
36084 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36085 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36086 getBox : function(){
36087 if(this.collapsed){
36088 return this.collapsedEl.getBox();
36090 var box = this.el.getBox();
36092 var sh = this.split.el.getHeight();
36099 updateBox : function(box){
36100 if(this.split && !this.collapsed){
36101 var sh = this.split.el.getHeight();
36104 this.split.el.setLeft(box.x);
36105 this.split.el.setTop(box.y-sh);
36106 this.split.el.setWidth(box.width);
36108 if(this.collapsed){
36109 this.updateBody(box.width, null);
36111 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36115 Roo.bootstrap.layout.East = function(config){
36116 config.region = "east";
36117 config.cursor = "e-resize";
36118 Roo.bootstrap.layout.Split.call(this, config);
36120 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36121 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36122 this.split.el.addClass("roo-layout-split-h");
36124 var size = config.initialSize || config.width;
36125 if(typeof size != "undefined"){
36126 this.el.setWidth(size);
36129 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36130 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36131 getBox : function(){
36132 if(this.collapsed){
36133 return this.collapsedEl.getBox();
36135 var box = this.el.getBox();
36137 var sw = this.split.el.getWidth();
36144 updateBox : function(box){
36145 if(this.split && !this.collapsed){
36146 var sw = this.split.el.getWidth();
36148 this.split.el.setLeft(box.x);
36149 this.split.el.setTop(box.y);
36150 this.split.el.setHeight(box.height);
36153 if(this.collapsed){
36154 this.updateBody(null, box.height);
36156 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36160 Roo.bootstrap.layout.West = function(config){
36161 config.region = "west";
36162 config.cursor = "w-resize";
36164 Roo.bootstrap.layout.Split.call(this, config);
36166 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36167 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36168 this.split.el.addClass("roo-layout-split-h");
36172 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36173 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36175 onRender: function(ctr, pos)
36177 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36178 var size = this.config.initialSize || this.config.width;
36179 if(typeof size != "undefined"){
36180 this.el.setWidth(size);
36184 getBox : function(){
36185 if(this.collapsed){
36186 return this.collapsedEl.getBox();
36188 var box = this.el.getBox();
36190 box.width += this.split.el.getWidth();
36195 updateBox : function(box){
36196 if(this.split && !this.collapsed){
36197 var sw = this.split.el.getWidth();
36199 this.split.el.setLeft(box.x+box.width);
36200 this.split.el.setTop(box.y);
36201 this.split.el.setHeight(box.height);
36203 if(this.collapsed){
36204 this.updateBody(null, box.height);
36206 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36209 Roo.namespace("Roo.bootstrap.panel");/*
36211 * Ext JS Library 1.1.1
36212 * Copyright(c) 2006-2007, Ext JS, LLC.
36214 * Originally Released Under LGPL - original licence link has changed is not relivant.
36217 * <script type="text/javascript">
36220 * @class Roo.ContentPanel
36221 * @extends Roo.util.Observable
36222 * A basic ContentPanel element.
36223 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36224 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36225 * @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
36226 * @cfg {Boolean} closable True if the panel can be closed/removed
36227 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36228 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36229 * @cfg {Toolbar} toolbar A toolbar for this panel
36230 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36231 * @cfg {String} title The title for this panel
36232 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36233 * @cfg {String} url Calls {@link #setUrl} with this value
36234 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36235 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36236 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36237 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36238 * @cfg {Boolean} badges render the badges
36241 * Create a new ContentPanel.
36242 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36243 * @param {String/Object} config A string to set only the title or a config object
36244 * @param {String} content (optional) Set the HTML content for this panel
36245 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36247 Roo.bootstrap.panel.Content = function( config){
36249 this.tpl = config.tpl || false;
36251 var el = config.el;
36252 var content = config.content;
36254 if(config.autoCreate){ // xtype is available if this is called from factory
36257 this.el = Roo.get(el);
36258 if(!this.el && config && config.autoCreate){
36259 if(typeof config.autoCreate == "object"){
36260 if(!config.autoCreate.id){
36261 config.autoCreate.id = config.id||el;
36263 this.el = Roo.DomHelper.append(document.body,
36264 config.autoCreate, true);
36266 var elcfg = { tag: "div",
36267 cls: "roo-layout-inactive-content",
36271 elcfg.html = config.html;
36275 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36278 this.closable = false;
36279 this.loaded = false;
36280 this.active = false;
36283 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36285 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36287 this.wrapEl = this.el; //this.el.wrap();
36289 if (config.toolbar.items) {
36290 ti = config.toolbar.items ;
36291 delete config.toolbar.items ;
36295 this.toolbar.render(this.wrapEl, 'before');
36296 for(var i =0;i < ti.length;i++) {
36297 // Roo.log(['add child', items[i]]);
36298 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36300 this.toolbar.items = nitems;
36301 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36302 delete config.toolbar;
36306 // xtype created footer. - not sure if will work as we normally have to render first..
36307 if (this.footer && !this.footer.el && this.footer.xtype) {
36308 if (!this.wrapEl) {
36309 this.wrapEl = this.el.wrap();
36312 this.footer.container = this.wrapEl.createChild();
36314 this.footer = Roo.factory(this.footer, Roo);
36319 if(typeof config == "string"){
36320 this.title = config;
36322 Roo.apply(this, config);
36326 this.resizeEl = Roo.get(this.resizeEl, true);
36328 this.resizeEl = this.el;
36330 // handle view.xtype
36338 * Fires when this panel is activated.
36339 * @param {Roo.ContentPanel} this
36343 * @event deactivate
36344 * Fires when this panel is activated.
36345 * @param {Roo.ContentPanel} this
36347 "deactivate" : true,
36351 * Fires when this panel is resized if fitToFrame is true.
36352 * @param {Roo.ContentPanel} this
36353 * @param {Number} width The width after any component adjustments
36354 * @param {Number} height The height after any component adjustments
36360 * Fires when this tab is created
36361 * @param {Roo.ContentPanel} this
36372 if(this.autoScroll){
36373 this.resizeEl.setStyle("overflow", "auto");
36375 // fix randome scrolling
36376 //this.el.on('scroll', function() {
36377 // Roo.log('fix random scolling');
36378 // this.scrollTo('top',0);
36381 content = content || this.content;
36383 this.setContent(content);
36385 if(config && config.url){
36386 this.setUrl(this.url, this.params, this.loadOnce);
36391 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36393 if (this.view && typeof(this.view.xtype) != 'undefined') {
36394 this.view.el = this.el.appendChild(document.createElement("div"));
36395 this.view = Roo.factory(this.view);
36396 this.view.render && this.view.render(false, '');
36400 this.fireEvent('render', this);
36403 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36407 setRegion : function(region){
36408 this.region = region;
36409 this.setActiveClass(region && !this.background);
36413 setActiveClass: function(state)
36416 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36417 this.el.setStyle('position','relative');
36419 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36420 this.el.setStyle('position', 'absolute');
36425 * Returns the toolbar for this Panel if one was configured.
36426 * @return {Roo.Toolbar}
36428 getToolbar : function(){
36429 return this.toolbar;
36432 setActiveState : function(active)
36434 this.active = active;
36435 this.setActiveClass(active);
36437 this.fireEvent("deactivate", this);
36439 this.fireEvent("activate", this);
36443 * Updates this panel's element
36444 * @param {String} content The new content
36445 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36447 setContent : function(content, loadScripts){
36448 this.el.update(content, loadScripts);
36451 ignoreResize : function(w, h){
36452 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36455 this.lastSize = {width: w, height: h};
36460 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36461 * @return {Roo.UpdateManager} The UpdateManager
36463 getUpdateManager : function(){
36464 return this.el.getUpdateManager();
36467 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36468 * @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:
36471 url: "your-url.php",
36472 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36473 callback: yourFunction,
36474 scope: yourObject, //(optional scope)
36477 text: "Loading...",
36482 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36483 * 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.
36484 * @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}
36485 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36486 * @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.
36487 * @return {Roo.ContentPanel} this
36490 var um = this.el.getUpdateManager();
36491 um.update.apply(um, arguments);
36497 * 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.
36498 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36499 * @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)
36500 * @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)
36501 * @return {Roo.UpdateManager} The UpdateManager
36503 setUrl : function(url, params, loadOnce){
36504 if(this.refreshDelegate){
36505 this.removeListener("activate", this.refreshDelegate);
36507 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36508 this.on("activate", this.refreshDelegate);
36509 return this.el.getUpdateManager();
36512 _handleRefresh : function(url, params, loadOnce){
36513 if(!loadOnce || !this.loaded){
36514 var updater = this.el.getUpdateManager();
36515 updater.update(url, params, this._setLoaded.createDelegate(this));
36519 _setLoaded : function(){
36520 this.loaded = true;
36524 * Returns this panel's id
36527 getId : function(){
36532 * Returns this panel's element - used by regiosn to add.
36533 * @return {Roo.Element}
36535 getEl : function(){
36536 return this.wrapEl || this.el;
36541 adjustForComponents : function(width, height)
36543 //Roo.log('adjustForComponents ');
36544 if(this.resizeEl != this.el){
36545 width -= this.el.getFrameWidth('lr');
36546 height -= this.el.getFrameWidth('tb');
36549 var te = this.toolbar.getEl();
36550 te.setWidth(width);
36551 height -= te.getHeight();
36554 var te = this.footer.getEl();
36555 te.setWidth(width);
36556 height -= te.getHeight();
36560 if(this.adjustments){
36561 width += this.adjustments[0];
36562 height += this.adjustments[1];
36564 return {"width": width, "height": height};
36567 setSize : function(width, height){
36568 if(this.fitToFrame && !this.ignoreResize(width, height)){
36569 if(this.fitContainer && this.resizeEl != this.el){
36570 this.el.setSize(width, height);
36572 var size = this.adjustForComponents(width, height);
36573 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36574 this.fireEvent('resize', this, size.width, size.height);
36579 * Returns this panel's title
36582 getTitle : function(){
36584 if (typeof(this.title) != 'object') {
36589 for (var k in this.title) {
36590 if (!this.title.hasOwnProperty(k)) {
36594 if (k.indexOf('-') >= 0) {
36595 var s = k.split('-');
36596 for (var i = 0; i<s.length; i++) {
36597 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36600 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36607 * Set this panel's title
36608 * @param {String} title
36610 setTitle : function(title){
36611 this.title = title;
36613 this.region.updatePanelTitle(this, title);
36618 * Returns true is this panel was configured to be closable
36619 * @return {Boolean}
36621 isClosable : function(){
36622 return this.closable;
36625 beforeSlide : function(){
36627 this.resizeEl.clip();
36630 afterSlide : function(){
36632 this.resizeEl.unclip();
36636 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36637 * Will fail silently if the {@link #setUrl} method has not been called.
36638 * This does not activate the panel, just updates its content.
36640 refresh : function(){
36641 if(this.refreshDelegate){
36642 this.loaded = false;
36643 this.refreshDelegate();
36648 * Destroys this panel
36650 destroy : function(){
36651 this.el.removeAllListeners();
36652 var tempEl = document.createElement("span");
36653 tempEl.appendChild(this.el.dom);
36654 tempEl.innerHTML = "";
36660 * form - if the content panel contains a form - this is a reference to it.
36661 * @type {Roo.form.Form}
36665 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36666 * This contains a reference to it.
36672 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36682 * @param {Object} cfg Xtype definition of item to add.
36686 getChildContainer: function () {
36687 return this.getEl();
36692 var ret = new Roo.factory(cfg);
36697 if (cfg.xtype.match(/^Form$/)) {
36700 //if (this.footer) {
36701 // el = this.footer.container.insertSibling(false, 'before');
36703 el = this.el.createChild();
36706 this.form = new Roo.form.Form(cfg);
36709 if ( this.form.allItems.length) {
36710 this.form.render(el.dom);
36714 // should only have one of theses..
36715 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36716 // views.. should not be just added - used named prop 'view''
36718 cfg.el = this.el.appendChild(document.createElement("div"));
36721 var ret = new Roo.factory(cfg);
36723 ret.render && ret.render(false, ''); // render blank..
36733 * @class Roo.bootstrap.panel.Grid
36734 * @extends Roo.bootstrap.panel.Content
36736 * Create a new GridPanel.
36737 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36738 * @param {Object} config A the config object
36744 Roo.bootstrap.panel.Grid = function(config)
36748 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36749 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36751 config.el = this.wrapper;
36752 //this.el = this.wrapper;
36754 if (config.container) {
36755 // ctor'ed from a Border/panel.grid
36758 this.wrapper.setStyle("overflow", "hidden");
36759 this.wrapper.addClass('roo-grid-container');
36764 if(config.toolbar){
36765 var tool_el = this.wrapper.createChild();
36766 this.toolbar = Roo.factory(config.toolbar);
36768 if (config.toolbar.items) {
36769 ti = config.toolbar.items ;
36770 delete config.toolbar.items ;
36774 this.toolbar.render(tool_el);
36775 for(var i =0;i < ti.length;i++) {
36776 // Roo.log(['add child', items[i]]);
36777 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36779 this.toolbar.items = nitems;
36781 delete config.toolbar;
36784 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36785 config.grid.scrollBody = true;;
36786 config.grid.monitorWindowResize = false; // turn off autosizing
36787 config.grid.autoHeight = false;
36788 config.grid.autoWidth = false;
36790 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36792 if (config.background) {
36793 // render grid on panel activation (if panel background)
36794 this.on('activate', function(gp) {
36795 if (!gp.grid.rendered) {
36796 gp.grid.render(this.wrapper);
36797 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36802 this.grid.render(this.wrapper);
36803 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
36806 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36807 // ??? needed ??? config.el = this.wrapper;
36812 // xtype created footer. - not sure if will work as we normally have to render first..
36813 if (this.footer && !this.footer.el && this.footer.xtype) {
36815 var ctr = this.grid.getView().getFooterPanel(true);
36816 this.footer.dataSource = this.grid.dataSource;
36817 this.footer = Roo.factory(this.footer, Roo);
36818 this.footer.render(ctr);
36828 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36829 getId : function(){
36830 return this.grid.id;
36834 * Returns the grid for this panel
36835 * @return {Roo.bootstrap.Table}
36837 getGrid : function(){
36841 setSize : function(width, height){
36842 if(!this.ignoreResize(width, height)){
36843 var grid = this.grid;
36844 var size = this.adjustForComponents(width, height);
36845 var gridel = grid.getGridEl();
36846 gridel.setSize(size.width, size.height);
36848 var thd = grid.getGridEl().select('thead',true).first();
36849 var tbd = grid.getGridEl().select('tbody', true).first();
36851 tbd.setSize(width, height - thd.getHeight());
36860 beforeSlide : function(){
36861 this.grid.getView().scroller.clip();
36864 afterSlide : function(){
36865 this.grid.getView().scroller.unclip();
36868 destroy : function(){
36869 this.grid.destroy();
36871 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
36876 * @class Roo.bootstrap.panel.Nest
36877 * @extends Roo.bootstrap.panel.Content
36879 * Create a new Panel, that can contain a layout.Border.
36882 * @param {Roo.BorderLayout} layout The layout for this panel
36883 * @param {String/Object} config A string to set only the title or a config object
36885 Roo.bootstrap.panel.Nest = function(config)
36887 // construct with only one argument..
36888 /* FIXME - implement nicer consturctors
36889 if (layout.layout) {
36891 layout = config.layout;
36892 delete config.layout;
36894 if (layout.xtype && !layout.getEl) {
36895 // then layout needs constructing..
36896 layout = Roo.factory(layout, Roo);
36900 config.el = config.layout.getEl();
36902 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36904 config.layout.monitorWindowResize = false; // turn off autosizing
36905 this.layout = config.layout;
36906 this.layout.getEl().addClass("roo-layout-nested-layout");
36913 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36915 setSize : function(width, height){
36916 if(!this.ignoreResize(width, height)){
36917 var size = this.adjustForComponents(width, height);
36918 var el = this.layout.getEl();
36919 if (size.height < 1) {
36920 el.setWidth(size.width);
36922 el.setSize(size.width, size.height);
36924 var touch = el.dom.offsetWidth;
36925 this.layout.layout();
36926 // ie requires a double layout on the first pass
36927 if(Roo.isIE && !this.initialized){
36928 this.initialized = true;
36929 this.layout.layout();
36934 // activate all subpanels if not currently active..
36936 setActiveState : function(active){
36937 this.active = active;
36938 this.setActiveClass(active);
36941 this.fireEvent("deactivate", this);
36945 this.fireEvent("activate", this);
36946 // not sure if this should happen before or after..
36947 if (!this.layout) {
36948 return; // should not happen..
36951 for (var r in this.layout.regions) {
36952 reg = this.layout.getRegion(r);
36953 if (reg.getActivePanel()) {
36954 //reg.showPanel(reg.getActivePanel()); // force it to activate..
36955 reg.setActivePanel(reg.getActivePanel());
36958 if (!reg.panels.length) {
36961 reg.showPanel(reg.getPanel(0));
36970 * Returns the nested BorderLayout for this panel
36971 * @return {Roo.BorderLayout}
36973 getLayout : function(){
36974 return this.layout;
36978 * Adds a xtype elements to the layout of the nested panel
36982 xtype : 'ContentPanel',
36989 xtype : 'NestedLayoutPanel',
36995 items : [ ... list of content panels or nested layout panels.. ]
36999 * @param {Object} cfg Xtype definition of item to add.
37001 addxtype : function(cfg) {
37002 return this.layout.addxtype(cfg);
37007 * Ext JS Library 1.1.1
37008 * Copyright(c) 2006-2007, Ext JS, LLC.
37010 * Originally Released Under LGPL - original licence link has changed is not relivant.
37013 * <script type="text/javascript">
37016 * @class Roo.TabPanel
37017 * @extends Roo.util.Observable
37018 * A lightweight tab container.
37022 // basic tabs 1, built from existing content
37023 var tabs = new Roo.TabPanel("tabs1");
37024 tabs.addTab("script", "View Script");
37025 tabs.addTab("markup", "View Markup");
37026 tabs.activate("script");
37028 // more advanced tabs, built from javascript
37029 var jtabs = new Roo.TabPanel("jtabs");
37030 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37032 // set up the UpdateManager
37033 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37034 var updater = tab2.getUpdateManager();
37035 updater.setDefaultUrl("ajax1.htm");
37036 tab2.on('activate', updater.refresh, updater, true);
37038 // Use setUrl for Ajax loading
37039 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37040 tab3.setUrl("ajax2.htm", null, true);
37043 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37046 jtabs.activate("jtabs-1");
37049 * Create a new TabPanel.
37050 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37051 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37053 Roo.bootstrap.panel.Tabs = function(config){
37055 * The container element for this TabPanel.
37056 * @type Roo.Element
37058 this.el = Roo.get(config.el);
37061 if(typeof config == "boolean"){
37062 this.tabPosition = config ? "bottom" : "top";
37064 Roo.apply(this, config);
37068 if(this.tabPosition == "bottom"){
37069 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37070 this.el.addClass("roo-tabs-bottom");
37072 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37073 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37074 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37076 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37078 if(this.tabPosition != "bottom"){
37079 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37080 * @type Roo.Element
37082 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37083 this.el.addClass("roo-tabs-top");
37087 this.bodyEl.setStyle("position", "relative");
37089 this.active = null;
37090 this.activateDelegate = this.activate.createDelegate(this);
37095 * Fires when the active tab changes
37096 * @param {Roo.TabPanel} this
37097 * @param {Roo.TabPanelItem} activePanel The new active tab
37101 * @event beforetabchange
37102 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37103 * @param {Roo.TabPanel} this
37104 * @param {Object} e Set cancel to true on this object to cancel the tab change
37105 * @param {Roo.TabPanelItem} tab The tab being changed to
37107 "beforetabchange" : true
37110 Roo.EventManager.onWindowResize(this.onResize, this);
37111 this.cpad = this.el.getPadding("lr");
37112 this.hiddenCount = 0;
37115 // toolbar on the tabbar support...
37116 if (this.toolbar) {
37117 alert("no toolbar support yet");
37118 this.toolbar = false;
37120 var tcfg = this.toolbar;
37121 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37122 this.toolbar = new Roo.Toolbar(tcfg);
37123 if (Roo.isSafari) {
37124 var tbl = tcfg.container.child('table', true);
37125 tbl.setAttribute('width', '100%');
37133 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37136 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37138 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37140 tabPosition : "top",
37142 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37144 currentTabWidth : 0,
37146 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37150 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37154 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37156 preferredTabWidth : 175,
37158 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37160 resizeTabs : false,
37162 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37164 monitorResize : true,
37166 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37171 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37172 * @param {String} id The id of the div to use <b>or create</b>
37173 * @param {String} text The text for the tab
37174 * @param {String} content (optional) Content to put in the TabPanelItem body
37175 * @param {Boolean} closable (optional) True to create a close icon on the tab
37176 * @return {Roo.TabPanelItem} The created TabPanelItem
37178 addTab : function(id, text, content, closable, tpl)
37180 var item = new Roo.bootstrap.panel.TabItem({
37184 closable : closable,
37187 this.addTabItem(item);
37189 item.setContent(content);
37195 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37196 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37197 * @return {Roo.TabPanelItem}
37199 getTab : function(id){
37200 return this.items[id];
37204 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37205 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37207 hideTab : function(id){
37208 var t = this.items[id];
37211 this.hiddenCount++;
37212 this.autoSizeTabs();
37217 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37218 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37220 unhideTab : function(id){
37221 var t = this.items[id];
37223 t.setHidden(false);
37224 this.hiddenCount--;
37225 this.autoSizeTabs();
37230 * Adds an existing {@link Roo.TabPanelItem}.
37231 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37233 addTabItem : function(item){
37234 this.items[item.id] = item;
37235 this.items.push(item);
37236 // if(this.resizeTabs){
37237 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37238 // this.autoSizeTabs();
37240 // item.autoSize();
37245 * Removes a {@link Roo.TabPanelItem}.
37246 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37248 removeTab : function(id){
37249 var items = this.items;
37250 var tab = items[id];
37251 if(!tab) { return; }
37252 var index = items.indexOf(tab);
37253 if(this.active == tab && items.length > 1){
37254 var newTab = this.getNextAvailable(index);
37259 this.stripEl.dom.removeChild(tab.pnode.dom);
37260 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37261 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37263 items.splice(index, 1);
37264 delete this.items[tab.id];
37265 tab.fireEvent("close", tab);
37266 tab.purgeListeners();
37267 this.autoSizeTabs();
37270 getNextAvailable : function(start){
37271 var items = this.items;
37273 // look for a next tab that will slide over to
37274 // replace the one being removed
37275 while(index < items.length){
37276 var item = items[++index];
37277 if(item && !item.isHidden()){
37281 // if one isn't found select the previous tab (on the left)
37284 var item = items[--index];
37285 if(item && !item.isHidden()){
37293 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37294 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37296 disableTab : function(id){
37297 var tab = this.items[id];
37298 if(tab && this.active != tab){
37304 * Enables a {@link Roo.TabPanelItem} that is disabled.
37305 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37307 enableTab : function(id){
37308 var tab = this.items[id];
37313 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37314 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37315 * @return {Roo.TabPanelItem} The TabPanelItem.
37317 activate : function(id){
37318 var tab = this.items[id];
37322 if(tab == this.active || tab.disabled){
37326 this.fireEvent("beforetabchange", this, e, tab);
37327 if(e.cancel !== true && !tab.disabled){
37329 this.active.hide();
37331 this.active = this.items[id];
37332 this.active.show();
37333 this.fireEvent("tabchange", this, this.active);
37339 * Gets the active {@link Roo.TabPanelItem}.
37340 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37342 getActiveTab : function(){
37343 return this.active;
37347 * Updates the tab body element to fit the height of the container element
37348 * for overflow scrolling
37349 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37351 syncHeight : function(targetHeight){
37352 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37353 var bm = this.bodyEl.getMargins();
37354 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37355 this.bodyEl.setHeight(newHeight);
37359 onResize : function(){
37360 if(this.monitorResize){
37361 this.autoSizeTabs();
37366 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37368 beginUpdate : function(){
37369 this.updating = true;
37373 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37375 endUpdate : function(){
37376 this.updating = false;
37377 this.autoSizeTabs();
37381 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37383 autoSizeTabs : function(){
37384 var count = this.items.length;
37385 var vcount = count - this.hiddenCount;
37386 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37389 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37390 var availWidth = Math.floor(w / vcount);
37391 var b = this.stripBody;
37392 if(b.getWidth() > w){
37393 var tabs = this.items;
37394 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37395 if(availWidth < this.minTabWidth){
37396 /*if(!this.sleft){ // incomplete scrolling code
37397 this.createScrollButtons();
37400 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37403 if(this.currentTabWidth < this.preferredTabWidth){
37404 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37410 * Returns the number of tabs in this TabPanel.
37413 getCount : function(){
37414 return this.items.length;
37418 * Resizes all the tabs to the passed width
37419 * @param {Number} The new width
37421 setTabWidth : function(width){
37422 this.currentTabWidth = width;
37423 for(var i = 0, len = this.items.length; i < len; i++) {
37424 if(!this.items[i].isHidden()) {
37425 this.items[i].setWidth(width);
37431 * Destroys this TabPanel
37432 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37434 destroy : function(removeEl){
37435 Roo.EventManager.removeResizeListener(this.onResize, this);
37436 for(var i = 0, len = this.items.length; i < len; i++){
37437 this.items[i].purgeListeners();
37439 if(removeEl === true){
37440 this.el.update("");
37445 createStrip : function(container)
37447 var strip = document.createElement("nav");
37448 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37449 container.appendChild(strip);
37453 createStripList : function(strip)
37455 // div wrapper for retard IE
37456 // returns the "tr" element.
37457 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37458 //'<div class="x-tabs-strip-wrap">'+
37459 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37460 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37461 return strip.firstChild; //.firstChild.firstChild.firstChild;
37463 createBody : function(container)
37465 var body = document.createElement("div");
37466 Roo.id(body, "tab-body");
37467 //Roo.fly(body).addClass("x-tabs-body");
37468 Roo.fly(body).addClass("tab-content");
37469 container.appendChild(body);
37472 createItemBody :function(bodyEl, id){
37473 var body = Roo.getDom(id);
37475 body = document.createElement("div");
37478 //Roo.fly(body).addClass("x-tabs-item-body");
37479 Roo.fly(body).addClass("tab-pane");
37480 bodyEl.insertBefore(body, bodyEl.firstChild);
37484 createStripElements : function(stripEl, text, closable, tpl)
37486 var td = document.createElement("li"); // was td..
37489 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37492 stripEl.appendChild(td);
37494 td.className = "x-tabs-closable";
37495 if(!this.closeTpl){
37496 this.closeTpl = new Roo.Template(
37497 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37498 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37499 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
37502 var el = this.closeTpl.overwrite(td, {"text": text});
37503 var close = el.getElementsByTagName("div")[0];
37504 var inner = el.getElementsByTagName("em")[0];
37505 return {"el": el, "close": close, "inner": inner};
37508 // not sure what this is..
37509 // if(!this.tabTpl){
37510 //this.tabTpl = new Roo.Template(
37511 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37512 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37514 // this.tabTpl = new Roo.Template(
37515 // '<a href="#">' +
37516 // '<span unselectable="on"' +
37517 // (this.disableTooltips ? '' : ' title="{text}"') +
37518 // ' >{text}</span></a>'
37524 var template = tpl || this.tabTpl || false;
37528 template = new Roo.Template(
37530 '<span unselectable="on"' +
37531 (this.disableTooltips ? '' : ' title="{text}"') +
37532 ' >{text}</span></a>'
37536 switch (typeof(template)) {
37540 template = new Roo.Template(template);
37546 var el = template.overwrite(td, {"text": text});
37548 var inner = el.getElementsByTagName("span")[0];
37550 return {"el": el, "inner": inner};
37558 * @class Roo.TabPanelItem
37559 * @extends Roo.util.Observable
37560 * Represents an individual item (tab plus body) in a TabPanel.
37561 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37562 * @param {String} id The id of this TabPanelItem
37563 * @param {String} text The text for the tab of this TabPanelItem
37564 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37566 Roo.bootstrap.panel.TabItem = function(config){
37568 * The {@link Roo.TabPanel} this TabPanelItem belongs to
37569 * @type Roo.TabPanel
37571 this.tabPanel = config.panel;
37573 * The id for this TabPanelItem
37576 this.id = config.id;
37578 this.disabled = false;
37580 this.text = config.text;
37582 this.loaded = false;
37583 this.closable = config.closable;
37586 * The body element for this TabPanelItem.
37587 * @type Roo.Element
37589 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37590 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37591 this.bodyEl.setStyle("display", "block");
37592 this.bodyEl.setStyle("zoom", "1");
37593 //this.hideAction();
37595 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37597 this.el = Roo.get(els.el);
37598 this.inner = Roo.get(els.inner, true);
37599 this.textEl = Roo.get(this.el.dom.firstChild, true);
37600 this.pnode = Roo.get(els.el.parentNode, true);
37601 this.el.on("mousedown", this.onTabMouseDown, this);
37602 this.el.on("click", this.onTabClick, this);
37604 if(config.closable){
37605 var c = Roo.get(els.close, true);
37606 c.dom.title = this.closeText;
37607 c.addClassOnOver("close-over");
37608 c.on("click", this.closeClick, this);
37614 * Fires when this tab becomes the active tab.
37615 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37616 * @param {Roo.TabPanelItem} this
37620 * @event beforeclose
37621 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37622 * @param {Roo.TabPanelItem} this
37623 * @param {Object} e Set cancel to true on this object to cancel the close.
37625 "beforeclose": true,
37628 * Fires when this tab is closed.
37629 * @param {Roo.TabPanelItem} this
37633 * @event deactivate
37634 * Fires when this tab is no longer the active tab.
37635 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37636 * @param {Roo.TabPanelItem} this
37638 "deactivate" : true
37640 this.hidden = false;
37642 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37645 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37647 purgeListeners : function(){
37648 Roo.util.Observable.prototype.purgeListeners.call(this);
37649 this.el.removeAllListeners();
37652 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37655 this.pnode.addClass("active");
37658 this.tabPanel.stripWrap.repaint();
37660 this.fireEvent("activate", this.tabPanel, this);
37664 * Returns true if this tab is the active tab.
37665 * @return {Boolean}
37667 isActive : function(){
37668 return this.tabPanel.getActiveTab() == this;
37672 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37675 this.pnode.removeClass("active");
37677 this.fireEvent("deactivate", this.tabPanel, this);
37680 hideAction : function(){
37681 this.bodyEl.hide();
37682 this.bodyEl.setStyle("position", "absolute");
37683 this.bodyEl.setLeft("-20000px");
37684 this.bodyEl.setTop("-20000px");
37687 showAction : function(){
37688 this.bodyEl.setStyle("position", "relative");
37689 this.bodyEl.setTop("");
37690 this.bodyEl.setLeft("");
37691 this.bodyEl.show();
37695 * Set the tooltip for the tab.
37696 * @param {String} tooltip The tab's tooltip
37698 setTooltip : function(text){
37699 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37700 this.textEl.dom.qtip = text;
37701 this.textEl.dom.removeAttribute('title');
37703 this.textEl.dom.title = text;
37707 onTabClick : function(e){
37708 e.preventDefault();
37709 this.tabPanel.activate(this.id);
37712 onTabMouseDown : function(e){
37713 e.preventDefault();
37714 this.tabPanel.activate(this.id);
37717 getWidth : function(){
37718 return this.inner.getWidth();
37721 setWidth : function(width){
37722 var iwidth = width - this.pnode.getPadding("lr");
37723 this.inner.setWidth(iwidth);
37724 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37725 this.pnode.setWidth(width);
37729 * Show or hide the tab
37730 * @param {Boolean} hidden True to hide or false to show.
37732 setHidden : function(hidden){
37733 this.hidden = hidden;
37734 this.pnode.setStyle("display", hidden ? "none" : "");
37738 * Returns true if this tab is "hidden"
37739 * @return {Boolean}
37741 isHidden : function(){
37742 return this.hidden;
37746 * Returns the text for this tab
37749 getText : function(){
37753 autoSize : function(){
37754 //this.el.beginMeasure();
37755 this.textEl.setWidth(1);
37757 * #2804 [new] Tabs in Roojs
37758 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37760 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37761 //this.el.endMeasure();
37765 * Sets the text for the tab (Note: this also sets the tooltip text)
37766 * @param {String} text The tab's text and tooltip
37768 setText : function(text){
37770 this.textEl.update(text);
37771 this.setTooltip(text);
37772 //if(!this.tabPanel.resizeTabs){
37773 // this.autoSize();
37777 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37779 activate : function(){
37780 this.tabPanel.activate(this.id);
37784 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37786 disable : function(){
37787 if(this.tabPanel.active != this){
37788 this.disabled = true;
37789 this.pnode.addClass("disabled");
37794 * Enables this TabPanelItem if it was previously disabled.
37796 enable : function(){
37797 this.disabled = false;
37798 this.pnode.removeClass("disabled");
37802 * Sets the content for this TabPanelItem.
37803 * @param {String} content The content
37804 * @param {Boolean} loadScripts true to look for and load scripts
37806 setContent : function(content, loadScripts){
37807 this.bodyEl.update(content, loadScripts);
37811 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37812 * @return {Roo.UpdateManager} The UpdateManager
37814 getUpdateManager : function(){
37815 return this.bodyEl.getUpdateManager();
37819 * Set a URL to be used to load the content for this TabPanelItem.
37820 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37821 * @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)
37822 * @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)
37823 * @return {Roo.UpdateManager} The UpdateManager
37825 setUrl : function(url, params, loadOnce){
37826 if(this.refreshDelegate){
37827 this.un('activate', this.refreshDelegate);
37829 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37830 this.on("activate", this.refreshDelegate);
37831 return this.bodyEl.getUpdateManager();
37835 _handleRefresh : function(url, params, loadOnce){
37836 if(!loadOnce || !this.loaded){
37837 var updater = this.bodyEl.getUpdateManager();
37838 updater.update(url, params, this._setLoaded.createDelegate(this));
37843 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
37844 * Will fail silently if the setUrl method has not been called.
37845 * This does not activate the panel, just updates its content.
37847 refresh : function(){
37848 if(this.refreshDelegate){
37849 this.loaded = false;
37850 this.refreshDelegate();
37855 _setLoaded : function(){
37856 this.loaded = true;
37860 closeClick : function(e){
37863 this.fireEvent("beforeclose", this, o);
37864 if(o.cancel !== true){
37865 this.tabPanel.removeTab(this.id);
37869 * The text displayed in the tooltip for the close icon.
37872 closeText : "Close this tab"
37882 * @class Roo.bootstrap.PhoneInput
37883 * @extends Roo.bootstrap.TriggerField
37884 * Bootstrap PhoneInput class
37887 * Create a new PhoneInput
37888 * @param {Object} config The config object
37891 Roo.bootstrap.PhoneInput = function(config){
37893 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37898 * Fires when the dropdown list is expanded
37899 * @param {Roo.bootstrap.ComboBox} combo This combo box
37904 * Fires when the dropdown list is collapsed
37905 * @param {Roo.bootstrap.ComboBox} combo This combo box
37909 * @event beforeselect
37910 * Fires before a list item is selected. Return false to cancel the selection.
37911 * @param {Roo.bootstrap.ComboBox} combo This combo box
37912 * @param {Roo.data.Record} record The data record returned from the underlying store
37913 * @param {Number} index The index of the selected item in the dropdown list
37915 'beforeselect' : true,
37918 * Fires when a list item is selected
37919 * @param {Roo.bootstrap.ComboBox} combo This combo box
37920 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37921 * @param {Number} index The index of the selected item in the dropdown list
37925 * @event beforequery
37926 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37927 * The event object passed has these properties:
37928 * @param {Roo.bootstrap.ComboBox} combo This combo box
37929 * @param {String} query The query
37930 * @param {Boolean} forceAll true to force "all" query
37931 * @param {Boolean} cancel true to cancel the query
37932 * @param {Object} e The query event object
37934 'beforequery': true,
37937 * Fires when the 'add' icon is pressed (add a listener to enable add button)
37938 * @param {Roo.bootstrap.ComboBox} combo This combo box
37943 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37944 * @param {Roo.bootstrap.ComboBox} combo This combo box
37945 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37950 * Fires when the remove value from the combobox array
37951 * @param {Roo.bootstrap.ComboBox} combo This combo box
37955 * @event afterremove
37956 * Fires when the remove value from the combobox array
37957 * @param {Roo.bootstrap.ComboBox} combo This combo box
37959 'afterremove' : true,
37961 * @event specialfilter
37962 * Fires when specialfilter
37963 * @param {Roo.bootstrap.ComboBox} combo This combo box
37965 'touchviewdisplay' : true
37968 this.country = []; //fetch country JSON
37971 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37973 listWidth: undefined,
37977 selectedClass: 'active',
37983 triggerAction: 'query',
37985 validClass : "has-success",
37987 invalidClass: "has-warning",
37990 defaultCountry: 'hk',
37992 preferedCountries: undefined, //array
37994 filterCountries: undefined, //array
37996 displayMode: undefined, //string
37998 getAutoCreate : function(){
38000 this.list = Roo.bootstrap.PhoneInput.List;
38002 if(this.filterCountries) {
38003 for(var i = 0; i < this.filterCountries.length; i++) {
38004 delete this.list[this.filterCountries[i]];
38008 if (this.preferedCountries) {
38012 var align = this.labelAlign || this.parentLabelAlign();
38014 var id = Roo.id(); //all el??
38023 type : this.inputType,
38024 cls : 'form-control',
38025 style: 'padding-left: 60px;',
38026 placeholder : this.placeholder || ''
38030 input.name = this.name;
38033 input.cls += ' input-' + this.size;
38036 if (this.disabled) {
38037 input.disabled=true;
38040 var inputblock = input;
38042 if(this.hasFeedback && !this.allowBlank){
38045 cls: 'glyphicon form-control-feedback'
38053 inputblock.cn.push(input);
38055 if(this.hasFeedback && !this.allowBlank){
38056 inputblock.cls += 'has-feedback';
38057 inputblock.cn.push(feedback);
38066 cls: 'form-hidden-field'
38075 style: 'margin-right:5px',
38076 cls: 'roo-selected-region',
38077 cn: [] //flag position ... (iti-flag-us)
38085 if (this.caret != false) {
38088 cls: 'fa fa-' + this.caret
38093 cls: 'roo-select2-container input-group',
38099 cls : 'input-group-addon btn dropdown-toggle',
38100 style : 'position: absolute; z-index: 4;background: none;border: none; margin-top: 4px; margin-left: 3px; margin-right: 3px;',
38106 cls: 'combobox-clear',
38117 combobox.cn.push(box);
38119 if (align ==='left' && this.fieldLabel.length) {
38121 cfg.cls += ' roo-form-group-label-left';
38126 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38127 tooltip : 'This field is required'
38132 cls : 'control-label',
38133 html : this.fieldLabel
38144 var labelCfg = cfg.cn[1];
38145 var contentCfg = cfg.cn[2];
38147 if(this.indicatorpos == 'right'){
38152 cls : 'control-label',
38156 html : this.fieldLabel
38160 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38161 tooltip : 'This field is required'
38174 labelCfg = cfg.cn[0];
38175 contentCfg = cfg.cn[1];
38178 if(this.labelWidth > 12){
38179 labelCfg.style = "width: " + this.labelWidth + 'px';
38182 if(this.labelWidth < 13 && this.labelmd == 0){
38183 this.labelmd = this.labelWidth;
38186 if(this.labellg > 0){
38187 labelCfg.cls += ' col-lg-' + this.labellg;
38188 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38191 if(this.labelmd > 0){
38192 labelCfg.cls += ' col-md-' + this.labelmd;
38193 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38196 if(this.labelsm > 0){
38197 labelCfg.cls += ' col-sm-' + this.labelsm;
38198 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38201 if(this.labelxs > 0){
38202 labelCfg.cls += ' col-xs-' + this.labelxs;
38203 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38206 } else if ( this.fieldLabel.length) {
38207 // Roo.log(" label");
38211 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38212 tooltip : 'This field is required'
38216 //cls : 'input-group-addon',
38217 html : this.fieldLabel
38225 if(this.indicatorpos == 'right'){
38233 html : this.fieldLabel
38237 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38238 tooltip : 'This field is required'
38250 ['xs','sm','md','lg'].map(function(size){
38251 if (settings[size]) {
38252 cfg.cls += ' col-' + size + '-' + settings[size];
38259 _initEventsCalled : false,
38261 initEvents: function()
38263 if (this._initEventsCalled) {
38267 this._initEventsCalled = true;
38269 this.store = new Roo.data.SimpleStore({
38271 fields : ['name','iso','dial_code','order','area_code']
38274 this.store = Roo.factory(this.store, Roo.data);
38275 this.store.parent = this;
38277 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38282 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38283 _this.list.setWidth(lw);
38286 this.list.on('mouseover', this.onViewOver, this);
38287 this.list.on('mousemove', this.onViewMove, this);
38288 this.list.on('scroll', this.onViewScroll, this);
38291 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38294 this.view = new Roo.View(this.list, this.tpl, {
38295 singleSelect:true, store: this.store, selectedClass: this.selectedClass
38298 this.view.on('click', this.onViewClick, this);
38300 this.store.on('beforeload', this.onBeforeLoad, this);
38301 this.store.on('load', this.onLoad, this);
38302 this.store.on('loadexception', this.onLoadException, this);
38304 this.keyNav = new Roo.KeyNav(this.inputEl(), {
38305 "up" : function(e){
38306 this.inKeyMode = true;
38310 "down" : function(e){
38311 if(!this.isExpanded()){
38312 this.onTriggerClick();
38314 this.inKeyMode = true;
38319 "enter" : function(e){
38320 // this.onViewClick();
38324 if(this.fireEvent("specialkey", this, e)){
38325 this.onViewClick(false);
38331 "esc" : function(e){
38335 "tab" : function(e){
38338 if(this.fireEvent("specialkey", this, e)){
38339 this.onViewClick(false);
38347 doRelay : function(foo, bar, hname){
38348 if(hname == 'down' || this.scope.isExpanded()){
38349 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38359 onViewOver : function(e, t){
38360 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38363 var item = this.view.findItemFromChild(t);
38366 var index = this.view.indexOf(item);
38367 this.select(index, false);
38371 onViewMove : function(e, t){
38372 this.inKeyMode = false;
38375 onViewScroll : function(e, t){
38377 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){
38381 this.hasQuery = true;
38383 this.loading = this.list.select('.loading', true).first();
38385 if(this.loading === null){
38386 this.list.createChild({
38388 cls: 'loading roo-select2-more-results roo-select2-active',
38389 html: 'Loading more results...'
38392 this.loading = this.list.select('.loading', true).first();
38394 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
38396 this.loading.hide();
38399 this.loading.show();
38404 this.loadNext = true;
38406 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
38411 onTriggerClick : function(e)
38413 Roo.log('trigger click');
38415 if(this.disabled || !this.triggerList){
38420 this.loadNext = false;
38422 if(this.isExpanded()){
38424 if (!this.blockFocus) {
38425 this.inputEl().focus();
38429 this.hasFocus = true;
38430 if(this.triggerAction == 'all') {
38431 this.doQuery(this.allQuery, true);
38433 this.doQuery(this.getRawValue());
38435 if (!this.blockFocus) {
38436 this.inputEl().focus();
38443 Roo.apply(Roo.bootstrap.PhoneInput, {
38446 * iso2 and abbr for all countries
38450 ["Afghanistan (افغانستان)", "af", "93"],
38451 ["Albania (Shqipëri)", "al", "355"],
38452 ["Algeria (الجزائر)", "dz", "213"],
38453 ["American Samoa", "as", "1684"],
38454 ["Andorra", "ad", "376"],
38455 ["Angola", "ao", "244"],
38456 ["Anguilla", "ai", "1264"],
38457 ["Antigua and Barbuda", "ag", "1268"],
38458 ["Argentina", "ar", "54"],
38459 ["Armenia (Հայաստան)", "am", "374"],
38460 ["Aruba", "aw", "297"],
38461 ["Australia", "au", "61", 0],
38462 ["Austria (Österreich)", "at", "43"],
38463 ["Azerbaijan (Azərbaycan)", "az", "994"],
38464 ["Bahamas", "bs", "1242"],
38465 ["Bahrain (البحرين)", "bh", "973"],
38466 ["Bangladesh (বাংলাদেশ)", "bd", "880"],
38467 ["Barbados", "bb", "1246"],
38468 ["Belarus (Беларусь)", "by", "375"],
38469 ["Belgium (België)", "be", "32"],
38470 ["Belize", "bz", "501"],
38471 ["Benin (Bénin)", "bj", "229"],
38472 ["Bermuda", "bm", "1441"],
38473 ["Bhutan (འབྲུག)", "bt", "975"],
38474 ["Bolivia", "bo", "591"],
38475 ["Bosnia and Herzegovina (Босна и Херцеговина)", "ba", "387"],
38476 ["Botswana", "bw", "267"],
38477 ["Brazil (Brasil)", "br", "55"],
38478 ["British Indian Ocean Territory", "io", "246"],
38479 ["British Virgin Islands", "vg", "1284"],
38480 ["Brunei", "bn", "673"],
38481 ["Bulgaria (България)", "bg", "359"],
38482 ["Burkina Faso", "bf", "226"],
38483 ["Burundi (Uburundi)", "bi", "257"],
38484 ["Cambodia (កម្ពុជា)", "kh", "855"],
38485 ["Cameroon (Cameroun)", "cm", "237"],
38486 ["Canada", "ca", "1", 1, ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]],
38487 ["Cape Verde (Kabu Verdi)", "cv", "238"],
38488 ["Caribbean Netherlands", "bq", "599", 1],
38489 ["Cayman Islands", "ky", "1345"],
38490 ["Central African Republic (République centrafricaine)", "cf", "236"],
38491 ["Chad (Tchad)", "td", "235"],
38492 ["Chile", "cl", "56"],
38493 ["China (中国)", "cn", "86"],
38494 ["Christmas Island", "cx", "61", 2],
38495 ["Cocos (Keeling) Islands", "cc", "61", 1],
38496 ["Colombia", "co", "57"],
38497 ["Comoros (جزر القمر)", "km", "269"],
38498 ["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)", "cd", "243"],
38499 ["Congo (Republic) (Congo-Brazzaville)", "cg", "242"],
38500 ["Cook Islands", "ck", "682"],
38501 ["Costa Rica", "cr", "506"],
38502 ["Côte d’Ivoire", "ci", "225"],
38503 ["Croatia (Hrvatska)", "hr", "385"],
38504 ["Cuba", "cu", "53"],
38505 ["Curaçao", "cw", "599", 0],
38506 ["Cyprus (Κύπρος)", "cy", "357"],
38507 ["Czech Republic (Česká republika)", "cz", "420"],
38508 ["Denmark (Danmark)", "dk", "45"],
38509 ["Djibouti", "dj", "253"],
38510 ["Dominica", "dm", "1767"],
38511 ["Dominican Republic (República Dominicana)", "do", "1", 2, ["809", "829", "849"]],
38512 ["Ecuador", "ec", "593"],
38513 ["Egypt (مصر)", "eg", "20"],
38514 ["El Salvador", "sv", "503"],
38515 ["Equatorial Guinea (Guinea Ecuatorial)", "gq", "240"],
38516 ["Eritrea", "er", "291"],
38517 ["Estonia (Eesti)", "ee", "372"],
38518 ["Ethiopia", "et", "251"],
38519 ["Falkland Islands (Islas Malvinas)", "fk", "500"],
38520 ["Faroe Islands (Føroyar)", "fo", "298"],
38521 ["Fiji", "fj", "679"],
38522 ["Finland (Suomi)", "fi", "358", 0],
38523 ["France", "fr", "33"],
38524 ["French Guiana (Guyane française)", "gf", "594"],
38525 ["French Polynesia (Polynésie française)", "pf", "689"],
38526 ["Gabon", "ga", "241"],
38527 ["Gambia", "gm", "220"],
38528 ["Georgia (საქართველო)", "ge", "995"],
38529 ["Germany (Deutschland)", "de", "49"],
38530 ["Ghana (Gaana)", "gh", "233"],
38531 ["Gibraltar", "gi", "350"],
38532 ["Greece (Ελλάδα)", "gr", "30"],
38533 ["Greenland (Kalaallit Nunaat)", "gl", "299"],
38534 ["Grenada", "gd", "1473"],
38535 ["Guadeloupe", "gp", "590", 0],
38536 ["Guam", "gu", "1671"],
38537 ["Guatemala", "gt", "502"],
38538 ["Guernsey", "gg", "44", 1],
38539 ["Guinea (Guinée)", "gn", "224"],
38540 ["Guinea-Bissau (Guiné Bissau)", "gw", "245"],
38541 ["Guyana", "gy", "592"],
38542 ["Haiti", "ht", "509"],
38543 ["Honduras", "hn", "504"],
38544 ["Hong Kong (香港)", "hk", "852"],
38545 ["Hungary (Magyarország)", "hu", "36"],
38546 ["Iceland (Ísland)", "is", "354"],
38547 ["India (भारत)", "in", "91"],
38548 ["Indonesia", "id", "62"],
38549 ["Iran (ایران)", "ir", "98"],
38550 ["Iraq (العراق)", "iq", "964"],
38551 ["Ireland", "ie", "353"],
38552 ["Isle of Man", "im", "44", 2],
38553 ["Israel (ישראל)", "il", "972"],
38554 ["Italy (Italia)", "it", "39", 0],
38555 ["Jamaica", "jm", "1876"],
38556 ["Japan (日本)", "jp", "81"],
38557 ["Jersey", "je", "44", 3],
38558 ["Jordan (الأردن)", "jo", "962"],
38559 ["Kazakhstan (Казахстан)", "kz", "7", 1],
38560 ["Kenya", "ke", "254"],
38561 ["Kiribati", "ki", "686"],
38562 ["Kosovo", "xk", "383"],
38563 ["Kuwait (الكويت)", "kw", "965"],
38564 ["Kyrgyzstan (Кыргызстан)", "kg", "996"],
38565 ["Laos (ລາວ)", "la", "856"],
38566 ["Latvia (Latvija)", "lv", "371"],
38567 ["Lebanon (لبنان)", "lb", "961"],
38568 ["Lesotho", "ls", "266"],
38569 ["Liberia", "lr", "231"],
38570 ["Libya (ليبيا)", "ly", "218"],
38571 ["Liechtenstein", "li", "423"],
38572 ["Lithuania (Lietuva)", "lt", "370"],
38573 ["Luxembourg", "lu", "352"],
38574 ["Macau (澳門)", "mo", "853"],
38575 ["Macedonia (FYROM) (Македонија)", "mk", "389"],
38576 ["Madagascar (Madagasikara)", "mg", "261"],
38577 ["Malawi", "mw", "265"],
38578 ["Malaysia", "my", "60"],
38579 ["Maldives", "mv", "960"],
38580 ["Mali", "ml", "223"],
38581 ["Malta", "mt", "356"],
38582 ["Marshall Islands", "mh", "692"],
38583 ["Martinique", "mq", "596"],
38584 ["Mauritania (موريتانيا)", "mr", "222"],
38585 ["Mauritius (Moris)", "mu", "230"],
38586 ["Mayotte", "yt", "262", 1],
38587 ["Mexico (México)", "mx", "52"],
38588 ["Micronesia", "fm", "691"],
38589 ["Moldova (Republica Moldova)", "md", "373"],
38590 ["Monaco", "mc", "377"],
38591 ["Mongolia (Монгол)", "mn", "976"],
38592 ["Montenegro (Crna Gora)", "me", "382"],
38593 ["Montserrat", "ms", "1664"],
38594 ["Morocco (المغرب)", "ma", "212", 0],
38595 ["Mozambique (Moçambique)", "mz", "258"],
38596 ["Myanmar (Burma) (မြန်မာ)", "mm", "95"],
38597 ["Namibia (Namibië)", "na", "264"],
38598 ["Nauru", "nr", "674"],
38599 ["Nepal (नेपाल)", "np", "977"],
38600 ["Netherlands (Nederland)", "nl", "31"],
38601 ["New Caledonia (Nouvelle-Calédonie)", "nc", "687"],
38602 ["New Zealand", "nz", "64"],
38603 ["Nicaragua", "ni", "505"],
38604 ["Niger (Nijar)", "ne", "227"],
38605 ["Nigeria", "ng", "234"],
38606 ["Niue", "nu", "683"],
38607 ["Norfolk Island", "nf", "672"],
38608 ["North Korea (조선 민주주의 인민 공화국)", "kp", "850"],
38609 ["Northern Mariana Islands", "mp", "1670"],
38610 ["Norway (Norge)", "no", "47", 0],
38611 ["Oman (عُمان)", "om", "968"],
38612 ["Pakistan (پاکستان)", "pk", "92"],
38613 ["Palau", "pw", "680"],
38614 ["Palestine (فلسطين)", "ps", "970"],
38615 ["Panama (Panamá)", "pa", "507"],
38616 ["Papua New Guinea", "pg", "675"],
38617 ["Paraguay", "py", "595"],
38618 ["Peru (Perú)", "pe", "51"],
38619 ["Philippines", "ph", "63"],
38620 ["Poland (Polska)", "pl", "48"],
38621 ["Portugal", "pt", "351"],
38622 ["Puerto Rico", "pr", "1", 3, ["787", "939"]],
38623 ["Qatar (قطر)", "qa", "974"],
38624 ["Réunion (La Réunion)", "re", "262", 0],
38625 ["Romania (România)", "ro", "40"],
38626 ["Russia (Россия)", "ru", "7", 0],
38627 ["Rwanda", "rw", "250"],
38628 ["Saint Barthélemy", "bl", "590", 1],
38629 ["Saint Helena", "sh", "290"],
38630 ["Saint Kitts and Nevis", "kn", "1869"],
38631 ["Saint Lucia", "lc", "1758"],
38632 ["Saint Martin (Saint-Martin (partie française))", "mf", "590", 2],
38633 ["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)", "pm", "508"],
38634 ["Saint Vincent and the Grenadines", "vc", "1784"],
38635 ["Samoa", "ws", "685"],
38636 ["San Marino", "sm", "378"],
38637 ["São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239"],
38638 ["Saudi Arabia (المملكة العربية السعودية)", "sa", "966"],
38639 ["Senegal (Sénégal)", "sn", "221"],
38640 ["Serbia (Србија)", "rs", "381"],
38641 ["Seychelles", "sc", "248"],
38642 ["Sierra Leone", "sl", "232"],
38643 ["Singapore", "sg", "65"],
38644 ["Sint Maarten", "sx", "1721"],
38645 ["Slovakia (Slovensko)", "sk", "421"],
38646 ["Slovenia (Slovenija)", "si", "386"],
38647 ["Solomon Islands", "sb", "677"],
38648 ["Somalia (Soomaaliya)", "so", "252"],
38649 ["South Africa", "za", "27"],
38650 ["South Korea (대한민국)", "kr", "82"],
38651 ["South Sudan (جنوب السودان)", "ss", "211"],
38652 ["Spain (España)", "es", "34"],
38653 ["Sri Lanka (ශ්රී ලංකාව)", "lk", "94"],
38654 ["Sudan (السودان)", "sd", "249"],
38655 ["Suriname", "sr", "597"],
38656 ["Svalbard and Jan Mayen", "sj", "47", 1],
38657 ["Swaziland", "sz", "268"],
38658 ["Sweden (Sverige)", "se", "46"],
38659 ["Switzerland (Schweiz)", "ch", "41"],
38660 ["Syria (سوريا)", "sy", "963"],
38661 ["Taiwan (台灣)", "tw", "886"],
38662 ["Tajikistan", "tj", "992"],
38663 ["Tanzania", "tz", "255"],
38664 ["Thailand (ไทย)", "th", "66"],
38665 ["Timor-Leste", "tl", "670"],
38666 ["Togo", "tg", "228"],
38667 ["Tokelau", "tk", "690"],
38668 ["Tonga", "to", "676"],
38669 ["Trinidad and Tobago", "tt", "1868"],
38670 ["Tunisia (تونس)", "tn", "216"],
38671 ["Turkey (Türkiye)", "tr", "90"],
38672 ["Turkmenistan", "tm", "993"],
38673 ["Turks and Caicos Islands", "tc", "1649"],
38674 ["Tuvalu", "tv", "688"],
38675 ["U.S. Virgin Islands", "vi", "1340"],
38676 ["Uganda", "ug", "256"],
38677 ["Ukraine (Україна)", "ua", "380"],
38678 ["United Arab Emirates (الإمارات العربية المتحدة)", "ae", "971"],
38679 ["United Kingdom", "gb", "44", 0],
38680 ["United States", "us", "1", 0],
38681 ["Uruguay", "uy", "598"],
38682 ["Uzbekistan (Oʻzbekiston)", "uz", "998"],
38683 ["Vanuatu", "vu", "678"],
38684 ["Vatican City (Città del Vaticano)", "va", "39", 1],
38685 ["Venezuela", "ve", "58"],
38686 ["Vietnam (Việt Nam)", "vn", "84"],
38687 ["Wallis and Futuna (Wallis-et-Futuna)", "wf", "681"],
38688 ["Western Sahara (الصحراء الغربية)", "eh", "212", 1],
38689 ["Yemen (اليمن)", "ye", "967"],
38690 ["Zambia", "zm", "260"],
38691 ["Zimbabwe", "zw", "263"],
38692 ["Åland Islands", "ax", "358", 1]