2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass(['hidden','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Element
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Element class
449 * @cfg {String} html contents of the element
450 * @cfg {String} tag tag of the element
451 * @cfg {String} cls class of the element
452 * @cfg {Boolean} preventDefault (true|false) default false
453 * @cfg {Boolean} clickable (true|false) default false
456 * Create a new Element
457 * @param {Object} config The config object
460 Roo.bootstrap.Element = function(config){
461 Roo.bootstrap.Element.superclass.constructor.call(this, config);
467 * When a element is chick
468 * @param {Roo.bootstrap.Element} this
469 * @param {Roo.EventObject} e
475 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
480 preventDefault: false,
483 getAutoCreate : function(){
487 // cls: this.cls, double assign in parent class Component.js :: onRender
494 initEvents: function()
496 Roo.bootstrap.Element.superclass.initEvents.call(this);
499 this.el.on('click', this.onClick, this);
504 onClick : function(e)
506 if(this.preventDefault){
510 this.fireEvent('click', this, e);
513 getValue : function()
515 return this.el.dom.innerHTML;
518 setValue : function(value)
520 this.el.dom.innerHTML = value;
535 * @class Roo.bootstrap.DropTarget
536 * @extends Roo.bootstrap.Element
537 * Bootstrap DropTarget class
539 * @cfg {string} name dropable name
542 * Create a new Dropable Area
543 * @param {Object} config The config object
546 Roo.bootstrap.DropTarget = function(config){
547 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
553 * When a element is chick
554 * @param {Roo.bootstrap.Element} this
555 * @param {Roo.EventObject} e
561 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
564 getAutoCreate : function(){
569 initEvents: function()
571 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
572 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
575 drop : this.dragDrop.createDelegate(this),
576 enter : this.dragEnter.createDelegate(this),
577 out : this.dragOut.createDelegate(this),
578 over : this.dragOver.createDelegate(this)
582 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
585 dragDrop : function(source,e,data)
587 // user has to decide how to impliment this.
590 //this.fireEvent('drop', this, source, e ,data);
594 dragEnter : function(n, dd, e, data)
596 // probably want to resize the element to match the dropped element..
598 this.originalSize = this.el.getSize();
599 this.el.setSize( n.el.getSize());
600 this.dropZone.DDM.refreshCache(this.name);
601 Roo.log([n, dd, e, data]);
604 dragOut : function(value)
606 // resize back to normal
608 this.el.setSize(this.originalSize);
609 this.dropZone.resetConstraints();
612 dragOver : function()
629 * @class Roo.bootstrap.Body
630 * @extends Roo.bootstrap.Component
631 * Bootstrap Body class
635 * @param {Object} config The config object
638 Roo.bootstrap.Body = function(config){
640 config = config || {};
642 Roo.bootstrap.Body.superclass.constructor.call(this, config);
643 this.el = Roo.get(config.el ? config.el : document.body );
644 if (this.cls && this.cls.length) {
645 Roo.get(document.body).addClass(this.cls);
649 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
651 is_body : true,// just to make sure it's constructed?
656 onRender : function(ct, position)
658 /* Roo.log("Roo.bootstrap.Body - onRender");
659 if (this.cls && this.cls.length) {
660 Roo.get(document.body).addClass(this.cls);
679 * @class Roo.bootstrap.ButtonGroup
680 * @extends Roo.bootstrap.Component
681 * Bootstrap ButtonGroup class
682 * @cfg {String} size lg | sm | xs (default empty normal)
683 * @cfg {String} align vertical | justified (default none)
684 * @cfg {String} direction up | down (default down)
685 * @cfg {Boolean} toolbar false | true
686 * @cfg {Boolean} btn true | false
691 * @param {Object} config The config object
694 Roo.bootstrap.ButtonGroup = function(config){
695 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
698 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
706 getAutoCreate : function(){
712 cfg.html = this.html || cfg.html;
723 if (['vertical','justified'].indexOf(this.align)!==-1) {
724 cfg.cls = 'btn-group-' + this.align;
726 if (this.align == 'justified') {
727 console.log(this.items);
731 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
732 cfg.cls += ' btn-group-' + this.size;
735 if (this.direction == 'up') {
736 cfg.cls += ' dropup' ;
742 * Add a button to the group (similar to NavItem API.)
744 addItem : function(cfg)
746 var cn = new Roo.bootstrap.Button(cfg);
748 cn.parentId = this.id;
749 cn.onRender(this.el, null);
763 * @class Roo.bootstrap.Button
764 * @extends Roo.bootstrap.Component
765 * Bootstrap Button class
766 * @cfg {String} html The button content
767 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
768 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
769 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
770 * @cfg {String} size ( lg | sm | xs)
771 * @cfg {String} tag ( a | input | submit)
772 * @cfg {String} href empty or href
773 * @cfg {Boolean} disabled default false;
774 * @cfg {Boolean} isClose default false;
775 * @cfg {String} glyphicon depricated - use fa
776 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
777 * @cfg {String} badge text for badge
778 * @cfg {String} theme (default|glow)
779 * @cfg {Boolean} inverse dark themed version
780 * @cfg {Boolean} toggle is it a slidy toggle button
781 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
782 * @cfg {String} ontext text for on slidy toggle state
783 * @cfg {String} offtext text for off slidy toggle state
784 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
785 * @cfg {Boolean} removeClass remove the standard class..
786 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
789 * Create a new button
790 * @param {Object} config The config object
794 Roo.bootstrap.Button = function(config){
795 Roo.bootstrap.Button.superclass.constructor.call(this, config);
796 this.weightClass = ["btn-default btn-outline-secondary",
808 * When a butotn is pressed
809 * @param {Roo.bootstrap.Button} btn
810 * @param {Roo.EventObject} e
815 * After the button has been toggles
816 * @param {Roo.bootstrap.Button} btn
817 * @param {Roo.EventObject} e
818 * @param {boolean} pressed (also available as button.pressed)
824 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
845 preventDefault: true,
853 getAutoCreate : function(){
861 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
862 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
867 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
869 if (this.toggle == true) {
872 cls: 'slider-frame roo-button',
877 'data-off-text':'OFF',
878 cls: 'slider-button',
884 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
885 cfg.cls += ' '+this.weight;
894 cfg["aria-hidden"] = true;
896 cfg.html = "×";
902 if (this.theme==='default') {
903 cfg.cls = 'btn roo-button';
905 //if (this.parentType != 'Navbar') {
906 this.weight = this.weight.length ? this.weight : 'default';
908 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
910 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
911 var weight = this.weight == 'default' ? 'secondary' : this.weight;
912 cfg.cls += ' btn-' + outline + weight;
913 if (this.weight == 'default') {
915 cfg.cls += ' btn-' + this.weight;
918 } else if (this.theme==='glow') {
921 cfg.cls = 'btn-glow roo-button';
923 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
925 cfg.cls += ' ' + this.weight;
931 this.cls += ' inverse';
935 if (this.active || this.pressed === true) {
936 cfg.cls += ' active';
940 cfg.disabled = 'disabled';
944 Roo.log('changing to ul' );
946 this.glyphicon = 'caret';
947 if (Roo.bootstrap.version == 4) {
948 this.fa = 'caret-down';
953 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
955 //gsRoo.log(this.parentType);
956 if (this.parentType === 'Navbar' && !this.parent().bar) {
957 Roo.log('changing to li?');
966 href : this.href || '#'
969 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
970 cfg.cls += ' dropdown';
977 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
979 if (this.glyphicon) {
980 cfg.html = ' ' + cfg.html;
985 cls: 'glyphicon glyphicon-' + this.glyphicon
990 cfg.html = ' ' + cfg.html;
995 cls: 'fa fas fa-' + this.fa
1005 // cfg.cls='btn roo-button';
1009 var value = cfg.html;
1014 cls: 'glyphicon glyphicon-' + this.glyphicon,
1021 cls: 'fa fas fa-' + this.fa,
1026 var bw = this.badge_weight.length ? this.badge_weight :
1027 (this.weight.length ? this.weight : 'secondary');
1028 bw = bw == 'default' ? 'secondary' : bw;
1034 cls: 'badge badge-' + bw,
1043 cfg.cls += ' dropdown';
1044 cfg.html = typeof(cfg.html) != 'undefined' ?
1045 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1048 if (cfg.tag !== 'a' && this.href !== '') {
1049 throw "Tag must be a to set href.";
1050 } else if (this.href.length > 0) {
1051 cfg.href = this.href;
1054 if(this.removeClass){
1059 cfg.target = this.target;
1064 initEvents: function() {
1065 // Roo.log('init events?');
1066 // Roo.log(this.el.dom);
1069 if (typeof (this.menu) != 'undefined') {
1070 this.menu.parentType = this.xtype;
1071 this.menu.triggerEl = this.el;
1072 this.addxtype(Roo.apply({}, this.menu));
1076 if (this.el.hasClass('roo-button')) {
1077 this.el.on('click', this.onClick, this);
1079 this.el.select('.roo-button').on('click', this.onClick, this);
1082 if(this.removeClass){
1083 this.el.on('click', this.onClick, this);
1086 this.el.enableDisplayMode();
1089 onClick : function(e)
1091 if (this.disabled) {
1095 Roo.log('button on click ');
1096 if(this.preventDefault){
1100 if (this.pressed === true || this.pressed === false) {
1101 this.toggleActive(e);
1105 this.fireEvent('click', this, e);
1109 * Enables this button
1113 this.disabled = false;
1114 this.el.removeClass('disabled');
1118 * Disable this button
1120 disable : function()
1122 this.disabled = true;
1123 this.el.addClass('disabled');
1126 * sets the active state on/off,
1127 * @param {Boolean} state (optional) Force a particular state
1129 setActive : function(v) {
1131 this.el[v ? 'addClass' : 'removeClass']('active');
1135 * toggles the current active state
1137 toggleActive : function(e)
1139 this.setActive(!this.pressed);
1140 this.fireEvent('toggle', this, e, !this.pressed);
1143 * get the current active state
1144 * @return {boolean} true if it's active
1146 isActive : function()
1148 return this.el.hasClass('active');
1151 * set the text of the first selected button
1153 setText : function(str)
1155 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1158 * get the text of the first selected button
1160 getText : function()
1162 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1165 setWeight : function(str)
1167 this.el.removeClass(this.weightClass);
1169 var outline = this.outline ? 'outline-' : '';
1170 if (str == 'default') {
1171 this.el.addClass('btn-default btn-outline-secondary');
1174 this.el.addClass('btn-' + outline + str);
1188 * @class Roo.bootstrap.Column
1189 * @extends Roo.bootstrap.Component
1190 * Bootstrap Column class
1191 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1192 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1193 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1194 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1195 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1196 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1197 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1198 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1201 * @cfg {Boolean} hidden (true|false) hide the element
1202 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1203 * @cfg {String} fa (ban|check|...) font awesome icon
1204 * @cfg {Number} fasize (1|2|....) font awsome size
1206 * @cfg {String} icon (info-sign|check|...) glyphicon name
1208 * @cfg {String} html content of column.
1211 * Create a new Column
1212 * @param {Object} config The config object
1215 Roo.bootstrap.Column = function(config){
1216 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1219 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1237 getAutoCreate : function(){
1238 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1246 var sizes = ['xs','sm','md','lg'];
1247 sizes.map(function(size ,ix){
1248 //Roo.log( size + ':' + settings[size]);
1250 if (settings[size+'off'] !== false) {
1251 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1254 if (settings[size] === false) {
1258 if (!settings[size]) { // 0 = hidden
1259 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1261 for (var i = ix; i > -1; i--) {
1262 cfg.cls += ' d-' + sizes[i] + '-none';
1268 cfg.cls += ' col-' + size + '-' + settings[size] + (
1269 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1275 cfg.cls += ' hidden';
1278 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1279 cfg.cls +=' alert alert-' + this.alert;
1283 if (this.html.length) {
1284 cfg.html = this.html;
1288 if (this.fasize > 1) {
1289 fasize = ' fa-' + this.fasize + 'x';
1291 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1296 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1315 * @class Roo.bootstrap.Container
1316 * @extends Roo.bootstrap.Component
1317 * Bootstrap Container class
1318 * @cfg {Boolean} jumbotron is it a jumbotron element
1319 * @cfg {String} html content of element
1320 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1321 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1322 * @cfg {String} header content of header (for panel)
1323 * @cfg {String} footer content of footer (for panel)
1324 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1325 * @cfg {String} tag (header|aside|section) type of HTML tag.
1326 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1327 * @cfg {String} fa font awesome icon
1328 * @cfg {String} icon (info-sign|check|...) glyphicon name
1329 * @cfg {Boolean} hidden (true|false) hide the element
1330 * @cfg {Boolean} expandable (true|false) default false
1331 * @cfg {Boolean} expanded (true|false) default true
1332 * @cfg {String} rheader contet on the right of header
1333 * @cfg {Boolean} clickable (true|false) default false
1337 * Create a new Container
1338 * @param {Object} config The config object
1341 Roo.bootstrap.Container = function(config){
1342 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1348 * After the panel has been expand
1350 * @param {Roo.bootstrap.Container} this
1355 * After the panel has been collapsed
1357 * @param {Roo.bootstrap.Container} this
1362 * When a element is chick
1363 * @param {Roo.bootstrap.Container} this
1364 * @param {Roo.EventObject} e
1370 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1388 getChildContainer : function() {
1394 if (this.panel.length) {
1395 return this.el.select('.panel-body',true).first();
1402 getAutoCreate : function(){
1405 tag : this.tag || 'div',
1409 if (this.jumbotron) {
1410 cfg.cls = 'jumbotron';
1415 // - this is applied by the parent..
1417 // cfg.cls = this.cls + '';
1420 if (this.sticky.length) {
1422 var bd = Roo.get(document.body);
1423 if (!bd.hasClass('bootstrap-sticky')) {
1424 bd.addClass('bootstrap-sticky');
1425 Roo.select('html',true).setStyle('height', '100%');
1428 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1432 if (this.well.length) {
1433 switch (this.well) {
1436 cfg.cls +=' well well-' +this.well;
1445 cfg.cls += ' hidden';
1449 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1450 cfg.cls +=' alert alert-' + this.alert;
1455 if (this.panel.length) {
1456 cfg.cls += ' panel panel-' + this.panel;
1458 if (this.header.length) {
1462 if(this.expandable){
1464 cfg.cls = cfg.cls + ' expandable';
1468 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1476 cls : 'panel-title',
1477 html : (this.expandable ? ' ' : '') + this.header
1481 cls: 'panel-header-right',
1487 cls : 'panel-heading',
1488 style : this.expandable ? 'cursor: pointer' : '',
1496 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1501 if (this.footer.length) {
1503 cls : 'panel-footer',
1512 body.html = this.html || cfg.html;
1513 // prefix with the icons..
1515 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1518 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1523 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1524 cfg.cls = 'container';
1530 initEvents: function()
1532 if(this.expandable){
1533 var headerEl = this.headerEl();
1536 headerEl.on('click', this.onToggleClick, this);
1541 this.el.on('click', this.onClick, this);
1546 onToggleClick : function()
1548 var headerEl = this.headerEl();
1564 if(this.fireEvent('expand', this)) {
1566 this.expanded = true;
1568 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1570 this.el.select('.panel-body',true).first().removeClass('hide');
1572 var toggleEl = this.toggleEl();
1578 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1583 collapse : function()
1585 if(this.fireEvent('collapse', this)) {
1587 this.expanded = false;
1589 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1590 this.el.select('.panel-body',true).first().addClass('hide');
1592 var toggleEl = this.toggleEl();
1598 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1602 toggleEl : function()
1604 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1608 return this.el.select('.panel-heading .fa',true).first();
1611 headerEl : function()
1613 if(!this.el || !this.panel.length || !this.header.length){
1617 return this.el.select('.panel-heading',true).first()
1622 if(!this.el || !this.panel.length){
1626 return this.el.select('.panel-body',true).first()
1629 titleEl : function()
1631 if(!this.el || !this.panel.length || !this.header.length){
1635 return this.el.select('.panel-title',true).first();
1638 setTitle : function(v)
1640 var titleEl = this.titleEl();
1646 titleEl.dom.innerHTML = v;
1649 getTitle : function()
1652 var titleEl = this.titleEl();
1658 return titleEl.dom.innerHTML;
1661 setRightTitle : function(v)
1663 var t = this.el.select('.panel-header-right',true).first();
1669 t.dom.innerHTML = v;
1672 onClick : function(e)
1676 this.fireEvent('click', this, e);
1683 * This is BS4's Card element.. - similar to our containers probably..
1687 * @class Roo.bootstrap.Card
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap Card class
1692 * possible... may not be implemented..
1693 * @cfg {String} header_image src url of image.
1694 * @cfg {String|Object} header
1695 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1697 * @cfg {String} title
1698 * @cfg {String} subtitle
1699 * @cfg {String} html -- html contents - or just use children..
1700 * @cfg {String} footer
1702 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1704 * @cfg {String} margin (0|1|2|3|4|5|auto)
1705 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1706 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1707 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1708 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1709 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1710 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1712 * @cfg {String} padding (0|1|2|3|4|5)
1713 * @cfg {String} padding_top (0|1|2|3|4|5)
1714 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1715 * @cfg {String} padding_left (0|1|2|3|4|5)
1716 * @cfg {String} padding_right (0|1|2|3|4|5)
1717 * @cfg {String} padding_x (0|1|2|3|4|5)
1718 * @cfg {String} padding_y (0|1|2|3|4|5)
1720 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1721 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1722 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1723 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1724 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1726 * @config {Boolean} dragable if this card can be dragged.
1727 * @config {String} drag_group group for drag
1728 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1729 * @config {String} drop_group group for drag
1733 * Create a new Container
1734 * @param {Object} config The config object
1737 Roo.bootstrap.Card = function(config){
1738 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1746 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1751 margin: '', /// may be better in component?
1785 childContainer : false,
1787 layoutCls : function()
1791 Roo.log(this.margin_bottom.length);
1792 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1793 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1795 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1796 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
1798 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1799 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
1803 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1804 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1805 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1809 // more generic support?
1817 // Roo.log("Call onRender: " + this.xtype);
1818 /* We are looking at something like this.
1820 <img src="..." class="card-img-top" alt="...">
1821 <div class="card-body">
1822 <h5 class="card-title">Card title</h5>
1823 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1825 >> this bit is really the body...
1826 <div> << we will ad dthis in hopefully it will not break shit.
1828 ** card text does not actually have any styling...
1830 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1833 <a href="#" class="card-link">Card link</a>
1836 <div class="card-footer">
1837 <small class="text-muted">Last updated 3 mins ago</small>
1841 getAutoCreate : function(){
1849 if (this.weight.length && this.weight != 'light') {
1850 cfg.cls += ' text-white';
1852 cfg.cls += ' text-dark'; // need as it's nested..
1854 if (this.weight.length) {
1855 cfg.cls += ' bg-' + this.weight;
1858 cfg.cls += this.layoutCls();
1860 if (this.header.length) {
1862 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1863 cls : 'card-header',
1864 html : this.header // escape?
1869 cls : 'card-header d-none'
1872 if (this.header_image.length) {
1875 cls : 'card-img-top',
1876 src: this.header_image // escape?
1881 cls : 'card-img-top d-none'
1892 if (this.title.length) {
1896 src: this.title // escape?
1900 if (this.subtitle.length) {
1904 src: this.subtitle // escape?
1910 cls : 'roo-card-body-ctr'
1913 if (this.html.length) {
1919 // fixme ? handle objects?
1920 if (this.footer.length) {
1923 cls : 'card-footer',
1924 html: this.footer // escape?
1933 getCardHeader : function()
1935 var ret = this.el.select('.card-header',true).first();
1936 if (ret.hasClass('d-none')) {
1937 ret.removeClass('d-none');
1943 getCardImageTop : function()
1945 var ret = this.el.select('.card-img-top',true).first();
1946 if (ret.hasClass('d-none')) {
1947 ret.removeClass('d-none');
1953 getChildContainer : function()
1959 return this.el.select('.roo-card-body-ctr',true).first();
1962 initEvents: function()
1965 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1966 containerScroll: true,
1967 ddGroup: this.drag_group || 'default_card_drag_group'
1969 this.dragZone.getDragData = this.getDragData.createDelegate(this);
1971 if (this.dropable) {
1972 this.dropZone = new Roo.dd.DropZone(this.getChildContainer(), {
1973 containerScroll: true,
1974 ddGroup: his.drop_group || 'default_card_drag_group'
1976 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
1977 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
1978 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
1979 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
1980 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
1985 getDragData : function(e) {
1986 var target = this.getEl();
1988 //this.handleSelection(e);
1993 nodes: this.getEl(),
1998 dragData.ddel = target.dom ; // the div element
1999 Roo.log(target.getWidth( ));
2000 dragData.ddel.style.width = target.getWidth() + 'px';
2012 * Card header - holder for the card header elements.
2017 * @class Roo.bootstrap.CardHeader
2018 * @extends Roo.bootstrap.Element
2019 * Bootstrap CardHeader class
2021 * Create a new Card Header - that you can embed children into
2022 * @param {Object} config The config object
2025 Roo.bootstrap.CardHeader = function(config){
2026 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2029 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2032 container_method : 'getCardHeader'
2045 * Card header - holder for the card header elements.
2050 * @class Roo.bootstrap.CardImageTop
2051 * @extends Roo.bootstrap.Element
2052 * Bootstrap CardImageTop class
2054 * Create a new Card Image Top container
2055 * @param {Object} config The config object
2058 Roo.bootstrap.CardImageTop = function(config){
2059 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2062 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2065 container_method : 'getCardImageTop'
2083 * @class Roo.bootstrap.Img
2084 * @extends Roo.bootstrap.Component
2085 * Bootstrap Img class
2086 * @cfg {Boolean} imgResponsive false | true
2087 * @cfg {String} border rounded | circle | thumbnail
2088 * @cfg {String} src image source
2089 * @cfg {String} alt image alternative text
2090 * @cfg {String} href a tag href
2091 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2092 * @cfg {String} xsUrl xs image source
2093 * @cfg {String} smUrl sm image source
2094 * @cfg {String} mdUrl md image source
2095 * @cfg {String} lgUrl lg image source
2098 * Create a new Input
2099 * @param {Object} config The config object
2102 Roo.bootstrap.Img = function(config){
2103 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2109 * The img click event for the img.
2110 * @param {Roo.EventObject} e
2116 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2118 imgResponsive: true,
2128 getAutoCreate : function()
2130 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2131 return this.createSingleImg();
2136 cls: 'roo-image-responsive-group',
2141 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2143 if(!_this[size + 'Url']){
2149 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2150 html: _this.html || cfg.html,
2151 src: _this[size + 'Url']
2154 img.cls += ' roo-image-responsive-' + size;
2156 var s = ['xs', 'sm', 'md', 'lg'];
2158 s.splice(s.indexOf(size), 1);
2160 Roo.each(s, function(ss){
2161 img.cls += ' hidden-' + ss;
2164 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2165 cfg.cls += ' img-' + _this.border;
2169 cfg.alt = _this.alt;
2182 a.target = _this.target;
2186 cfg.cn.push((_this.href) ? a : img);
2193 createSingleImg : function()
2197 cls: (this.imgResponsive) ? 'img-responsive' : '',
2199 src : 'about:blank' // just incase src get's set to undefined?!?
2202 cfg.html = this.html || cfg.html;
2204 cfg.src = this.src || cfg.src;
2206 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2207 cfg.cls += ' img-' + this.border;
2224 a.target = this.target;
2229 return (this.href) ? a : cfg;
2232 initEvents: function()
2235 this.el.on('click', this.onClick, this);
2240 onClick : function(e)
2242 Roo.log('img onclick');
2243 this.fireEvent('click', this, e);
2246 * Sets the url of the image - used to update it
2247 * @param {String} url the url of the image
2250 setSrc : function(url)
2254 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2255 this.el.dom.src = url;
2259 this.el.select('img', true).first().dom.src = url;
2275 * @class Roo.bootstrap.Link
2276 * @extends Roo.bootstrap.Component
2277 * Bootstrap Link Class
2278 * @cfg {String} alt image alternative text
2279 * @cfg {String} href a tag href
2280 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2281 * @cfg {String} html the content of the link.
2282 * @cfg {String} anchor name for the anchor link
2283 * @cfg {String} fa - favicon
2285 * @cfg {Boolean} preventDefault (true | false) default false
2289 * Create a new Input
2290 * @param {Object} config The config object
2293 Roo.bootstrap.Link = function(config){
2294 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2300 * The img click event for the img.
2301 * @param {Roo.EventObject} e
2307 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2311 preventDefault: false,
2317 getAutoCreate : function()
2319 var html = this.html || '';
2321 if (this.fa !== false) {
2322 html = '<i class="fa fa-' + this.fa + '"></i>';
2327 // anchor's do not require html/href...
2328 if (this.anchor === false) {
2330 cfg.href = this.href || '#';
2332 cfg.name = this.anchor;
2333 if (this.html !== false || this.fa !== false) {
2336 if (this.href !== false) {
2337 cfg.href = this.href;
2341 if(this.alt !== false){
2346 if(this.target !== false) {
2347 cfg.target = this.target;
2353 initEvents: function() {
2355 if(!this.href || this.preventDefault){
2356 this.el.on('click', this.onClick, this);
2360 onClick : function(e)
2362 if(this.preventDefault){
2365 //Roo.log('img onclick');
2366 this.fireEvent('click', this, e);
2379 * @class Roo.bootstrap.Header
2380 * @extends Roo.bootstrap.Component
2381 * Bootstrap Header class
2382 * @cfg {String} html content of header
2383 * @cfg {Number} level (1|2|3|4|5|6) default 1
2386 * Create a new Header
2387 * @param {Object} config The config object
2391 Roo.bootstrap.Header = function(config){
2392 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2395 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2403 getAutoCreate : function(){
2408 tag: 'h' + (1 *this.level),
2409 html: this.html || ''
2421 * Ext JS Library 1.1.1
2422 * Copyright(c) 2006-2007, Ext JS, LLC.
2424 * Originally Released Under LGPL - original licence link has changed is not relivant.
2427 * <script type="text/javascript">
2431 * @class Roo.bootstrap.MenuMgr
2432 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2435 Roo.bootstrap.MenuMgr = function(){
2436 var menus, active, groups = {}, attached = false, lastShow = new Date();
2438 // private - called when first menu is created
2441 active = new Roo.util.MixedCollection();
2442 Roo.get(document).addKeyListener(27, function(){
2443 if(active.length > 0){
2451 if(active && active.length > 0){
2452 var c = active.clone();
2462 if(active.length < 1){
2463 Roo.get(document).un("mouseup", onMouseDown);
2471 var last = active.last();
2472 lastShow = new Date();
2475 Roo.get(document).on("mouseup", onMouseDown);
2480 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2481 m.parentMenu.activeChild = m;
2482 }else if(last && last.isVisible()){
2483 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2488 function onBeforeHide(m){
2490 m.activeChild.hide();
2492 if(m.autoHideTimer){
2493 clearTimeout(m.autoHideTimer);
2494 delete m.autoHideTimer;
2499 function onBeforeShow(m){
2500 var pm = m.parentMenu;
2501 if(!pm && !m.allowOtherMenus){
2503 }else if(pm && pm.activeChild && active != m){
2504 pm.activeChild.hide();
2508 // private this should really trigger on mouseup..
2509 function onMouseDown(e){
2510 Roo.log("on Mouse Up");
2512 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2513 Roo.log("MenuManager hideAll");
2522 function onBeforeCheck(mi, state){
2524 var g = groups[mi.group];
2525 for(var i = 0, l = g.length; i < l; i++){
2527 g[i].setChecked(false);
2536 * Hides all menus that are currently visible
2538 hideAll : function(){
2543 register : function(menu){
2547 menus[menu.id] = menu;
2548 menu.on("beforehide", onBeforeHide);
2549 menu.on("hide", onHide);
2550 menu.on("beforeshow", onBeforeShow);
2551 menu.on("show", onShow);
2553 if(g && menu.events["checkchange"]){
2557 groups[g].push(menu);
2558 menu.on("checkchange", onCheck);
2563 * Returns a {@link Roo.menu.Menu} object
2564 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2565 * be used to generate and return a new Menu instance.
2567 get : function(menu){
2568 if(typeof menu == "string"){ // menu id
2570 }else if(menu.events){ // menu instance
2573 /*else if(typeof menu.length == 'number'){ // array of menu items?
2574 return new Roo.bootstrap.Menu({items:menu});
2575 }else{ // otherwise, must be a config
2576 return new Roo.bootstrap.Menu(menu);
2583 unregister : function(menu){
2584 delete menus[menu.id];
2585 menu.un("beforehide", onBeforeHide);
2586 menu.un("hide", onHide);
2587 menu.un("beforeshow", onBeforeShow);
2588 menu.un("show", onShow);
2590 if(g && menu.events["checkchange"]){
2591 groups[g].remove(menu);
2592 menu.un("checkchange", onCheck);
2597 registerCheckable : function(menuItem){
2598 var g = menuItem.group;
2603 groups[g].push(menuItem);
2604 menuItem.on("beforecheckchange", onBeforeCheck);
2609 unregisterCheckable : function(menuItem){
2610 var g = menuItem.group;
2612 groups[g].remove(menuItem);
2613 menuItem.un("beforecheckchange", onBeforeCheck);
2625 * @class Roo.bootstrap.Menu
2626 * @extends Roo.bootstrap.Component
2627 * Bootstrap Menu class - container for MenuItems
2628 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2629 * @cfg {bool} hidden if the menu should be hidden when rendered.
2630 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2631 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2635 * @param {Object} config The config object
2639 Roo.bootstrap.Menu = function(config){
2640 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2641 if (this.registerMenu && this.type != 'treeview') {
2642 Roo.bootstrap.MenuMgr.register(this);
2649 * Fires before this menu is displayed (return false to block)
2650 * @param {Roo.menu.Menu} this
2655 * Fires before this menu is hidden (return false to block)
2656 * @param {Roo.menu.Menu} this
2661 * Fires after this menu is displayed
2662 * @param {Roo.menu.Menu} this
2667 * Fires after this menu is hidden
2668 * @param {Roo.menu.Menu} this
2673 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2674 * @param {Roo.menu.Menu} this
2675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2676 * @param {Roo.EventObject} e
2681 * Fires when the mouse is hovering over this menu
2682 * @param {Roo.menu.Menu} this
2683 * @param {Roo.EventObject} e
2684 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2689 * Fires when the mouse exits this menu
2690 * @param {Roo.menu.Menu} this
2691 * @param {Roo.EventObject} e
2692 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2697 * Fires when a menu item contained in this menu is clicked
2698 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2699 * @param {Roo.EventObject} e
2703 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2706 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2710 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2713 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2715 registerMenu : true,
2717 menuItems :false, // stores the menu items..
2727 getChildContainer : function() {
2731 getAutoCreate : function(){
2733 //if (['right'].indexOf(this.align)!==-1) {
2734 // cfg.cn[1].cls += ' pull-right'
2740 cls : 'dropdown-menu' ,
2741 style : 'z-index:1000'
2745 if (this.type === 'submenu') {
2746 cfg.cls = 'submenu active';
2748 if (this.type === 'treeview') {
2749 cfg.cls = 'treeview-menu';
2754 initEvents : function() {
2756 // Roo.log("ADD event");
2757 // Roo.log(this.triggerEl.dom);
2759 this.triggerEl.on('click', this.onTriggerClick, this);
2761 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2764 if (this.triggerEl.hasClass('nav-item')) {
2765 // dropdown toggle on the 'a' in BS4?
2766 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2768 this.triggerEl.addClass('dropdown-toggle');
2771 this.el.on('touchstart' , this.onTouch, this);
2773 this.el.on('click' , this.onClick, this);
2775 this.el.on("mouseover", this.onMouseOver, this);
2776 this.el.on("mouseout", this.onMouseOut, this);
2780 findTargetItem : function(e)
2782 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2786 //Roo.log(t); Roo.log(t.id);
2788 //Roo.log(this.menuitems);
2789 return this.menuitems.get(t.id);
2791 //return this.items.get(t.menuItemId);
2797 onTouch : function(e)
2799 Roo.log("menu.onTouch");
2800 //e.stopEvent(); this make the user popdown broken
2804 onClick : function(e)
2806 Roo.log("menu.onClick");
2808 var t = this.findTargetItem(e);
2809 if(!t || t.isContainer){
2814 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2815 if(t == this.activeItem && t.shouldDeactivate(e)){
2816 this.activeItem.deactivate();
2817 delete this.activeItem;
2821 this.setActiveItem(t, true);
2829 Roo.log('pass click event');
2833 this.fireEvent("click", this, t, e);
2837 if(!t.href.length || t.href == '#'){
2838 (function() { _this.hide(); }).defer(100);
2843 onMouseOver : function(e){
2844 var t = this.findTargetItem(e);
2847 // if(t.canActivate && !t.disabled){
2848 // this.setActiveItem(t, true);
2852 this.fireEvent("mouseover", this, e, t);
2854 isVisible : function(){
2855 return !this.hidden;
2857 onMouseOut : function(e){
2858 var t = this.findTargetItem(e);
2861 // if(t == this.activeItem && t.shouldDeactivate(e)){
2862 // this.activeItem.deactivate();
2863 // delete this.activeItem;
2866 this.fireEvent("mouseout", this, e, t);
2871 * Displays this menu relative to another element
2872 * @param {String/HTMLElement/Roo.Element} element The element to align to
2873 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2874 * the element (defaults to this.defaultAlign)
2875 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2877 show : function(el, pos, parentMenu)
2879 if (false === this.fireEvent("beforeshow", this)) {
2880 Roo.log("show canceled");
2883 this.parentMenu = parentMenu;
2888 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2891 * Displays this menu at a specific xy position
2892 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2893 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2895 showAt : function(xy, parentMenu, /* private: */_e){
2896 this.parentMenu = parentMenu;
2901 this.fireEvent("beforeshow", this);
2902 //xy = this.el.adjustForConstraints(xy);
2906 this.hideMenuItems();
2907 this.hidden = false;
2908 this.triggerEl.addClass('open');
2909 this.el.addClass('show');
2911 // reassign x when hitting right
2912 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2913 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2916 // reassign y when hitting bottom
2917 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2918 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2921 // but the list may align on trigger left or trigger top... should it be a properity?
2923 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2928 this.fireEvent("show", this);
2934 this.doFocus.defer(50, this);
2938 doFocus : function(){
2940 this.focusEl.focus();
2945 * Hides this menu and optionally all parent menus
2946 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2948 hide : function(deep)
2950 if (false === this.fireEvent("beforehide", this)) {
2951 Roo.log("hide canceled");
2954 this.hideMenuItems();
2955 if(this.el && this.isVisible()){
2957 if(this.activeItem){
2958 this.activeItem.deactivate();
2959 this.activeItem = null;
2961 this.triggerEl.removeClass('open');;
2962 this.el.removeClass('show');
2964 this.fireEvent("hide", this);
2966 if(deep === true && this.parentMenu){
2967 this.parentMenu.hide(true);
2971 onTriggerClick : function(e)
2973 Roo.log('trigger click');
2975 var target = e.getTarget();
2977 Roo.log(target.nodeName.toLowerCase());
2979 if(target.nodeName.toLowerCase() === 'i'){
2985 onTriggerPress : function(e)
2987 Roo.log('trigger press');
2988 //Roo.log(e.getTarget());
2989 // Roo.log(this.triggerEl.dom);
2991 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2992 var pel = Roo.get(e.getTarget());
2993 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2994 Roo.log('is treeview or dropdown?');
2998 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3002 if (this.isVisible()) {
3007 this.show(this.triggerEl, '?', false);
3010 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3017 hideMenuItems : function()
3019 Roo.log("hide Menu Items");
3024 this.el.select('.open',true).each(function(aa) {
3026 aa.removeClass('open');
3030 addxtypeChild : function (tree, cntr) {
3031 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3033 this.menuitems.add(comp);
3045 this.getEl().dom.innerHTML = '';
3046 this.menuitems.clear();
3060 * @class Roo.bootstrap.MenuItem
3061 * @extends Roo.bootstrap.Component
3062 * Bootstrap MenuItem class
3063 * @cfg {String} html the menu label
3064 * @cfg {String} href the link
3065 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3066 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3067 * @cfg {Boolean} active used on sidebars to highlight active itesm
3068 * @cfg {String} fa favicon to show on left of menu item.
3069 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3073 * Create a new MenuItem
3074 * @param {Object} config The config object
3078 Roo.bootstrap.MenuItem = function(config){
3079 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3084 * The raw click event for the entire grid.
3085 * @param {Roo.bootstrap.MenuItem} this
3086 * @param {Roo.EventObject} e
3092 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3096 preventDefault: false,
3097 isContainer : false,
3101 getAutoCreate : function(){
3103 if(this.isContainer){
3106 cls: 'dropdown-menu-item '
3116 cls : 'dropdown-item',
3121 if (this.fa !== false) {
3124 cls : 'fa fa-' + this.fa
3133 cls: 'dropdown-menu-item',
3136 if (this.parent().type == 'treeview') {
3137 cfg.cls = 'treeview-menu';
3140 cfg.cls += ' active';
3145 anc.href = this.href || cfg.cn[0].href ;
3146 ctag.html = this.html || cfg.cn[0].html ;
3150 initEvents: function()
3152 if (this.parent().type == 'treeview') {
3153 this.el.select('a').on('click', this.onClick, this);
3157 this.menu.parentType = this.xtype;
3158 this.menu.triggerEl = this.el;
3159 this.menu = this.addxtype(Roo.apply({}, this.menu));
3163 onClick : function(e)
3165 Roo.log('item on click ');
3167 if(this.preventDefault){
3170 //this.parent().hideMenuItems();
3172 this.fireEvent('click', this, e);
3191 * @class Roo.bootstrap.MenuSeparator
3192 * @extends Roo.bootstrap.Component
3193 * Bootstrap MenuSeparator class
3196 * Create a new MenuItem
3197 * @param {Object} config The config object
3201 Roo.bootstrap.MenuSeparator = function(config){
3202 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3205 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3207 getAutoCreate : function(){
3226 * @class Roo.bootstrap.Modal
3227 * @extends Roo.bootstrap.Component
3228 * Bootstrap Modal class
3229 * @cfg {String} title Title of dialog
3230 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3231 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3232 * @cfg {Boolean} specificTitle default false
3233 * @cfg {Array} buttons Array of buttons or standard button set..
3234 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3235 * @cfg {Boolean} animate default true
3236 * @cfg {Boolean} allow_close default true
3237 * @cfg {Boolean} fitwindow default false
3238 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3239 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3240 * @cfg {String} size (sm|lg) default empty
3241 * @cfg {Number} max_width set the max width of modal
3245 * Create a new Modal Dialog
3246 * @param {Object} config The config object
3249 Roo.bootstrap.Modal = function(config){
3250 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3255 * The raw btnclick event for the button
3256 * @param {Roo.EventObject} e
3261 * Fire when dialog resize
3262 * @param {Roo.bootstrap.Modal} this
3263 * @param {Roo.EventObject} e
3267 this.buttons = this.buttons || [];
3270 this.tmpl = Roo.factory(this.tmpl);
3275 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3277 title : 'test dialog',
3287 specificTitle: false,
3289 buttonPosition: 'right',
3312 onRender : function(ct, position)
3314 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3317 var cfg = Roo.apply({}, this.getAutoCreate());
3320 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3322 //if (!cfg.name.length) {
3326 cfg.cls += ' ' + this.cls;
3329 cfg.style = this.style;
3331 this.el = Roo.get(document.body).createChild(cfg, position);
3333 //var type = this.el.dom.type;
3336 if(this.tabIndex !== undefined){
3337 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3340 this.dialogEl = this.el.select('.modal-dialog',true).first();
3341 this.bodyEl = this.el.select('.modal-body',true).first();
3342 this.closeEl = this.el.select('.modal-header .close', true).first();
3343 this.headerEl = this.el.select('.modal-header',true).first();
3344 this.titleEl = this.el.select('.modal-title',true).first();
3345 this.footerEl = this.el.select('.modal-footer',true).first();
3347 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3349 //this.el.addClass("x-dlg-modal");
3351 if (this.buttons.length) {
3352 Roo.each(this.buttons, function(bb) {
3353 var b = Roo.apply({}, bb);
3354 b.xns = b.xns || Roo.bootstrap;
3355 b.xtype = b.xtype || 'Button';
3356 if (typeof(b.listeners) == 'undefined') {
3357 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3360 var btn = Roo.factory(b);
3362 btn.render(this.getButtonContainer());
3366 // render the children.
3369 if(typeof(this.items) != 'undefined'){
3370 var items = this.items;
3373 for(var i =0;i < items.length;i++) {
3374 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3378 this.items = nitems;
3380 // where are these used - they used to be body/close/footer
3384 //this.el.addClass([this.fieldClass, this.cls]);
3388 getAutoCreate : function()
3390 // we will default to modal-body-overflow - might need to remove or make optional later.
3392 cls : 'modal-body enable-modal-body-overflow ',
3393 html : this.html || ''
3398 cls : 'modal-title',
3402 if(this.specificTitle){
3408 if (this.allow_close && Roo.bootstrap.version == 3) {
3418 if (this.allow_close && Roo.bootstrap.version == 4) {
3428 if(this.size.length){
3429 size = 'modal-' + this.size;
3432 var footer = Roo.bootstrap.version == 3 ?
3434 cls : 'modal-footer',
3438 cls: 'btn-' + this.buttonPosition
3443 { // BS4 uses mr-auto on left buttons....
3444 cls : 'modal-footer'
3455 cls: "modal-dialog " + size,
3458 cls : "modal-content",
3461 cls : 'modal-header',
3476 modal.cls += ' fade';
3482 getChildContainer : function() {
3487 getButtonContainer : function() {
3489 return Roo.bootstrap.version == 4 ?
3490 this.el.select('.modal-footer',true).first()
3491 : this.el.select('.modal-footer div',true).first();
3494 initEvents : function()
3496 if (this.allow_close) {
3497 this.closeEl.on('click', this.hide, this);
3499 Roo.EventManager.onWindowResize(this.resize, this, true);
3507 this.maskEl.setSize(
3508 Roo.lib.Dom.getViewWidth(true),
3509 Roo.lib.Dom.getViewHeight(true)
3512 if (this.fitwindow) {
3516 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3517 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3522 if(this.max_width !== 0) {
3524 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3527 this.setSize(w, this.height);
3531 if(this.max_height) {
3532 this.setSize(w,Math.min(
3534 Roo.lib.Dom.getViewportHeight(true) - 60
3540 if(!this.fit_content) {
3541 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3545 this.setSize(w, Math.min(
3547 this.headerEl.getHeight() +
3548 this.footerEl.getHeight() +
3549 this.getChildHeight(this.bodyEl.dom.childNodes),
3550 Roo.lib.Dom.getViewportHeight(true) - 60)
3556 setSize : function(w,h)
3567 if (!this.rendered) {
3571 //this.el.setStyle('display', 'block');
3572 this.el.removeClass('hideing');
3573 this.el.dom.style.display='block';
3575 Roo.get(document.body).addClass('modal-open');
3577 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3580 this.el.addClass('show');
3581 this.el.addClass('in');
3584 this.el.addClass('show');
3585 this.el.addClass('in');
3588 // not sure how we can show data in here..
3590 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3593 Roo.get(document.body).addClass("x-body-masked");
3595 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3596 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3597 this.maskEl.dom.style.display = 'block';
3598 this.maskEl.addClass('show');
3603 this.fireEvent('show', this);
3605 // set zindex here - otherwise it appears to be ignored...
3606 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3609 this.items.forEach( function(e) {
3610 e.layout ? e.layout() : false;
3618 if(this.fireEvent("beforehide", this) !== false){
3620 this.maskEl.removeClass('show');
3622 this.maskEl.dom.style.display = '';
3623 Roo.get(document.body).removeClass("x-body-masked");
3624 this.el.removeClass('in');
3625 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3627 if(this.animate){ // why
3628 this.el.addClass('hideing');
3629 this.el.removeClass('show');
3631 if (!this.el.hasClass('hideing')) {
3632 return; // it's been shown again...
3635 this.el.dom.style.display='';
3637 Roo.get(document.body).removeClass('modal-open');
3638 this.el.removeClass('hideing');
3642 this.el.removeClass('show');
3643 this.el.dom.style.display='';
3644 Roo.get(document.body).removeClass('modal-open');
3647 this.fireEvent('hide', this);
3650 isVisible : function()
3653 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3657 addButton : function(str, cb)
3661 var b = Roo.apply({}, { html : str } );
3662 b.xns = b.xns || Roo.bootstrap;
3663 b.xtype = b.xtype || 'Button';
3664 if (typeof(b.listeners) == 'undefined') {
3665 b.listeners = { click : cb.createDelegate(this) };
3668 var btn = Roo.factory(b);
3670 btn.render(this.getButtonContainer());
3676 setDefaultButton : function(btn)
3678 //this.el.select('.modal-footer').()
3681 resizeTo: function(w,h)
3683 this.dialogEl.setWidth(w);
3685 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3687 this.bodyEl.setHeight(h - diff);
3689 this.fireEvent('resize', this);
3692 setContentSize : function(w, h)
3696 onButtonClick: function(btn,e)
3699 this.fireEvent('btnclick', btn.name, e);
3702 * Set the title of the Dialog
3703 * @param {String} str new Title
3705 setTitle: function(str) {
3706 this.titleEl.dom.innerHTML = str;
3709 * Set the body of the Dialog
3710 * @param {String} str new Title
3712 setBody: function(str) {
3713 this.bodyEl.dom.innerHTML = str;
3716 * Set the body of the Dialog using the template
3717 * @param {Obj} data - apply this data to the template and replace the body contents.
3719 applyBody: function(obj)
3722 Roo.log("Error - using apply Body without a template");
3725 this.tmpl.overwrite(this.bodyEl, obj);
3728 getChildHeight : function(child_nodes)
3732 child_nodes.length == 0
3737 var child_height = 0;
3739 for(var i = 0; i < child_nodes.length; i++) {
3742 * for modal with tabs...
3743 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3745 var layout_childs = child_nodes[i].childNodes;
3747 for(var j = 0; j < layout_childs.length; j++) {
3749 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3751 var layout_body_childs = layout_childs[j].childNodes;
3753 for(var k = 0; k < layout_body_childs.length; k++) {
3755 if(layout_body_childs[k].classList.contains('navbar')) {
3756 child_height += layout_body_childs[k].offsetHeight;
3760 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3762 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3764 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3766 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3767 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3782 child_height += child_nodes[i].offsetHeight;
3783 // Roo.log(child_nodes[i].offsetHeight);
3786 return child_height;
3792 Roo.apply(Roo.bootstrap.Modal, {
3794 * Button config that displays a single OK button
3803 * Button config that displays Yes and No buttons
3819 * Button config that displays OK and Cancel buttons
3834 * Button config that displays Yes, No and Cancel buttons
3858 * messagebox - can be used as a replace
3862 * @class Roo.MessageBox
3863 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3867 Roo.Msg.alert('Status', 'Changes saved successfully.');
3869 // Prompt for user data:
3870 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3872 // process text value...
3876 // Show a dialog using config options:
3878 title:'Save Changes?',
3879 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3880 buttons: Roo.Msg.YESNOCANCEL,
3887 Roo.bootstrap.MessageBox = function(){
3888 var dlg, opt, mask, waitTimer;
3889 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3890 var buttons, activeTextEl, bwidth;
3894 var handleButton = function(button){
3896 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3900 var handleHide = function(){
3902 dlg.el.removeClass(opt.cls);
3905 // Roo.TaskMgr.stop(waitTimer);
3906 // waitTimer = null;
3911 var updateButtons = function(b){
3914 buttons["ok"].hide();
3915 buttons["cancel"].hide();
3916 buttons["yes"].hide();
3917 buttons["no"].hide();
3918 dlg.footerEl.hide();
3922 dlg.footerEl.show();
3923 for(var k in buttons){
3924 if(typeof buttons[k] != "function"){
3927 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3928 width += buttons[k].el.getWidth()+15;
3938 var handleEsc = function(d, k, e){
3939 if(opt && opt.closable !== false){
3949 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3950 * @return {Roo.BasicDialog} The BasicDialog element
3952 getDialog : function(){
3954 dlg = new Roo.bootstrap.Modal( {
3957 //constraintoviewport:false,
3959 //collapsible : false,
3964 //buttonAlign:"center",
3965 closeClick : function(){
3966 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3969 handleButton("cancel");
3974 dlg.on("hide", handleHide);
3976 //dlg.addKeyListener(27, handleEsc);
3978 this.buttons = buttons;
3979 var bt = this.buttonText;
3980 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3981 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3982 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3983 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3985 bodyEl = dlg.bodyEl.createChild({
3987 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3988 '<textarea class="roo-mb-textarea"></textarea>' +
3989 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3991 msgEl = bodyEl.dom.firstChild;
3992 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3993 textboxEl.enableDisplayMode();
3994 textboxEl.addKeyListener([10,13], function(){
3995 if(dlg.isVisible() && opt && opt.buttons){
3998 }else if(opt.buttons.yes){
3999 handleButton("yes");
4003 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4004 textareaEl.enableDisplayMode();
4005 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4006 progressEl.enableDisplayMode();
4008 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4009 var pf = progressEl.dom.firstChild;
4011 pp = Roo.get(pf.firstChild);
4012 pp.setHeight(pf.offsetHeight);
4020 * Updates the message box body text
4021 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4022 * the XHTML-compliant non-breaking space character '&#160;')
4023 * @return {Roo.MessageBox} This message box
4025 updateText : function(text)
4027 if(!dlg.isVisible() && !opt.width){
4028 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4029 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4031 msgEl.innerHTML = text || ' ';
4033 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4034 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4036 Math.min(opt.width || cw , this.maxWidth),
4037 Math.max(opt.minWidth || this.minWidth, bwidth)
4040 activeTextEl.setWidth(w);
4042 if(dlg.isVisible()){
4043 dlg.fixedcenter = false;
4045 // to big, make it scroll. = But as usual stupid IE does not support
4048 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4049 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4050 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4052 bodyEl.dom.style.height = '';
4053 bodyEl.dom.style.overflowY = '';
4056 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4058 bodyEl.dom.style.overflowX = '';
4061 dlg.setContentSize(w, bodyEl.getHeight());
4062 if(dlg.isVisible()){
4063 dlg.fixedcenter = true;
4069 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4070 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4071 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4072 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4073 * @return {Roo.MessageBox} This message box
4075 updateProgress : function(value, text){
4077 this.updateText(text);
4080 if (pp) { // weird bug on my firefox - for some reason this is not defined
4081 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4082 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4088 * Returns true if the message box is currently displayed
4089 * @return {Boolean} True if the message box is visible, else false
4091 isVisible : function(){
4092 return dlg && dlg.isVisible();
4096 * Hides the message box if it is displayed
4099 if(this.isVisible()){
4105 * Displays a new message box, or reinitializes an existing message box, based on the config options
4106 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4107 * The following config object properties are supported:
4109 Property Type Description
4110 ---------- --------------- ------------------------------------------------------------------------------------
4111 animEl String/Element An id or Element from which the message box should animate as it opens and
4112 closes (defaults to undefined)
4113 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4114 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4115 closable Boolean False to hide the top-right close button (defaults to true). Note that
4116 progress and wait dialogs will ignore this property and always hide the
4117 close button as they can only be closed programmatically.
4118 cls String A custom CSS class to apply to the message box element
4119 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4120 displayed (defaults to 75)
4121 fn Function A callback function to execute after closing the dialog. The arguments to the
4122 function will be btn (the name of the button that was clicked, if applicable,
4123 e.g. "ok"), and text (the value of the active text field, if applicable).
4124 Progress and wait dialogs will ignore this option since they do not respond to
4125 user actions and can only be closed programmatically, so any required function
4126 should be called by the same code after it closes the dialog.
4127 icon String A CSS class that provides a background image to be used as an icon for
4128 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4129 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4130 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4131 modal Boolean False to allow user interaction with the page while the message box is
4132 displayed (defaults to true)
4133 msg String A string that will replace the existing message box body text (defaults
4134 to the XHTML-compliant non-breaking space character ' ')
4135 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4136 progress Boolean True to display a progress bar (defaults to false)
4137 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4138 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4139 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4140 title String The title text
4141 value String The string value to set into the active textbox element if displayed
4142 wait Boolean True to display a progress bar (defaults to false)
4143 width Number The width of the dialog in pixels
4150 msg: 'Please enter your address:',
4152 buttons: Roo.MessageBox.OKCANCEL,
4155 animEl: 'addAddressBtn'
4158 * @param {Object} config Configuration options
4159 * @return {Roo.MessageBox} This message box
4161 show : function(options)
4164 // this causes nightmares if you show one dialog after another
4165 // especially on callbacks..
4167 if(this.isVisible()){
4170 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4171 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4172 Roo.log("New Dialog Message:" + options.msg )
4173 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4174 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4177 var d = this.getDialog();
4179 d.setTitle(opt.title || " ");
4180 d.closeEl.setDisplayed(opt.closable !== false);
4181 activeTextEl = textboxEl;
4182 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4187 textareaEl.setHeight(typeof opt.multiline == "number" ?
4188 opt.multiline : this.defaultTextHeight);
4189 activeTextEl = textareaEl;
4198 progressEl.setDisplayed(opt.progress === true);
4200 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4202 this.updateProgress(0);
4203 activeTextEl.dom.value = opt.value || "";
4205 dlg.setDefaultButton(activeTextEl);
4207 var bs = opt.buttons;
4211 }else if(bs && bs.yes){
4212 db = buttons["yes"];
4214 dlg.setDefaultButton(db);
4216 bwidth = updateButtons(opt.buttons);
4217 this.updateText(opt.msg);
4219 d.el.addClass(opt.cls);
4221 d.proxyDrag = opt.proxyDrag === true;
4222 d.modal = opt.modal !== false;
4223 d.mask = opt.modal !== false ? mask : false;
4225 // force it to the end of the z-index stack so it gets a cursor in FF
4226 document.body.appendChild(dlg.el.dom);
4227 d.animateTarget = null;
4228 d.show(options.animEl);
4234 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4235 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4236 * and closing the message box when the process is complete.
4237 * @param {String} title The title bar text
4238 * @param {String} msg The message box body text
4239 * @return {Roo.MessageBox} This message box
4241 progress : function(title, msg){
4248 minWidth: this.minProgressWidth,
4255 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4256 * If a callback function is passed it will be called after the user clicks the button, and the
4257 * id of the button that was clicked will be passed as the only parameter to the callback
4258 * (could also be the top-right close button).
4259 * @param {String} title The title bar text
4260 * @param {String} msg The message box body text
4261 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4262 * @param {Object} scope (optional) The scope of the callback function
4263 * @return {Roo.MessageBox} This message box
4265 alert : function(title, msg, fn, scope)
4280 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4281 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4282 * You are responsible for closing the message box when the process is complete.
4283 * @param {String} msg The message box body text
4284 * @param {String} title (optional) The title bar text
4285 * @return {Roo.MessageBox} This message box
4287 wait : function(msg, title){
4298 waitTimer = Roo.TaskMgr.start({
4300 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4308 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4309 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4310 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4311 * @param {String} title The title bar text
4312 * @param {String} msg The message box body text
4313 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4314 * @param {Object} scope (optional) The scope of the callback function
4315 * @return {Roo.MessageBox} This message box
4317 confirm : function(title, msg, fn, scope){
4321 buttons: this.YESNO,
4330 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4331 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4332 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4333 * (could also be the top-right close button) and the text that was entered will be passed as the two
4334 * parameters to the callback.
4335 * @param {String} title The title bar text
4336 * @param {String} msg The message box body text
4337 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4338 * @param {Object} scope (optional) The scope of the callback function
4339 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4340 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4341 * @return {Roo.MessageBox} This message box
4343 prompt : function(title, msg, fn, scope, multiline){
4347 buttons: this.OKCANCEL,
4352 multiline: multiline,
4359 * Button config that displays a single OK button
4364 * Button config that displays Yes and No buttons
4367 YESNO : {yes:true, no:true},
4369 * Button config that displays OK and Cancel buttons
4372 OKCANCEL : {ok:true, cancel:true},
4374 * Button config that displays Yes, No and Cancel buttons
4377 YESNOCANCEL : {yes:true, no:true, cancel:true},
4380 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4383 defaultTextHeight : 75,
4385 * The maximum width in pixels of the message box (defaults to 600)
4390 * The minimum width in pixels of the message box (defaults to 100)
4395 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4396 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4399 minProgressWidth : 250,
4401 * An object containing the default button text strings that can be overriden for localized language support.
4402 * Supported properties are: ok, cancel, yes and no.
4403 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4416 * Shorthand for {@link Roo.MessageBox}
4418 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4419 Roo.Msg = Roo.Msg || Roo.MessageBox;
4428 * @class Roo.bootstrap.Navbar
4429 * @extends Roo.bootstrap.Component
4430 * Bootstrap Navbar class
4433 * Create a new Navbar
4434 * @param {Object} config The config object
4438 Roo.bootstrap.Navbar = function(config){
4439 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4443 * @event beforetoggle
4444 * Fire before toggle the menu
4445 * @param {Roo.EventObject} e
4447 "beforetoggle" : true
4451 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4460 getAutoCreate : function(){
4463 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4467 initEvents :function ()
4469 //Roo.log(this.el.select('.navbar-toggle',true));
4470 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4477 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4479 var size = this.el.getSize();
4480 this.maskEl.setSize(size.width, size.height);
4481 this.maskEl.enableDisplayMode("block");
4490 getChildContainer : function()
4492 if (this.el && this.el.select('.collapse').getCount()) {
4493 return this.el.select('.collapse',true).first();
4508 onToggle : function()
4511 if(this.fireEvent('beforetoggle', this) === false){
4514 var ce = this.el.select('.navbar-collapse',true).first();
4516 if (!ce.hasClass('show')) {
4526 * Expand the navbar pulldown
4528 expand : function ()
4531 var ce = this.el.select('.navbar-collapse',true).first();
4532 if (ce.hasClass('collapsing')) {
4535 ce.dom.style.height = '';
4537 ce.addClass('in'); // old...
4538 ce.removeClass('collapse');
4539 ce.addClass('show');
4540 var h = ce.getHeight();
4542 ce.removeClass('show');
4543 // at this point we should be able to see it..
4544 ce.addClass('collapsing');
4546 ce.setHeight(0); // resize it ...
4547 ce.on('transitionend', function() {
4548 //Roo.log('done transition');
4549 ce.removeClass('collapsing');
4550 ce.addClass('show');
4551 ce.removeClass('collapse');
4553 ce.dom.style.height = '';
4554 }, this, { single: true} );
4556 ce.dom.scrollTop = 0;
4559 * Collapse the navbar pulldown
4561 collapse : function()
4563 var ce = this.el.select('.navbar-collapse',true).first();
4565 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4566 // it's collapsed or collapsing..
4569 ce.removeClass('in'); // old...
4570 ce.setHeight(ce.getHeight());
4571 ce.removeClass('show');
4572 ce.addClass('collapsing');
4574 ce.on('transitionend', function() {
4575 ce.dom.style.height = '';
4576 ce.removeClass('collapsing');
4577 ce.addClass('collapse');
4578 }, this, { single: true} );
4598 * @class Roo.bootstrap.NavSimplebar
4599 * @extends Roo.bootstrap.Navbar
4600 * Bootstrap Sidebar class
4602 * @cfg {Boolean} inverse is inverted color
4604 * @cfg {String} type (nav | pills | tabs)
4605 * @cfg {Boolean} arrangement stacked | justified
4606 * @cfg {String} align (left | right) alignment
4608 * @cfg {Boolean} main (true|false) main nav bar? default false
4609 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4611 * @cfg {String} tag (header|footer|nav|div) default is nav
4613 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4617 * Create a new Sidebar
4618 * @param {Object} config The config object
4622 Roo.bootstrap.NavSimplebar = function(config){
4623 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4626 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4642 getAutoCreate : function(){
4646 tag : this.tag || 'div',
4647 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4649 if (['light','white'].indexOf(this.weight) > -1) {
4650 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4652 cfg.cls += ' bg-' + this.weight;
4655 cfg.cls += ' navbar-inverse';
4659 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4661 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4670 cls: 'nav nav-' + this.xtype,
4676 this.type = this.type || 'nav';
4677 if (['tabs','pills'].indexOf(this.type) != -1) {
4678 cfg.cn[0].cls += ' nav-' + this.type
4682 if (this.type!=='nav') {
4683 Roo.log('nav type must be nav/tabs/pills')
4685 cfg.cn[0].cls += ' navbar-nav'
4691 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4692 cfg.cn[0].cls += ' nav-' + this.arrangement;
4696 if (this.align === 'right') {
4697 cfg.cn[0].cls += ' navbar-right';
4722 * navbar-expand-md fixed-top
4726 * @class Roo.bootstrap.NavHeaderbar
4727 * @extends Roo.bootstrap.NavSimplebar
4728 * Bootstrap Sidebar class
4730 * @cfg {String} brand what is brand
4731 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4732 * @cfg {String} brand_href href of the brand
4733 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4734 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4735 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4736 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4739 * Create a new Sidebar
4740 * @param {Object} config The config object
4744 Roo.bootstrap.NavHeaderbar = function(config){
4745 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4749 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4756 desktopCenter : false,
4759 getAutoCreate : function(){
4762 tag: this.nav || 'nav',
4763 cls: 'navbar navbar-expand-md',
4769 if (this.desktopCenter) {
4770 cn.push({cls : 'container', cn : []});
4778 cls: 'navbar-toggle navbar-toggler',
4779 'data-toggle': 'collapse',
4784 html: 'Toggle navigation'
4788 cls: 'icon-bar navbar-toggler-icon'
4801 cn.push( Roo.bootstrap.version == 4 ? btn : {
4803 cls: 'navbar-header',
4812 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4816 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4818 if (['light','white'].indexOf(this.weight) > -1) {
4819 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4821 cfg.cls += ' bg-' + this.weight;
4824 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4825 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4827 // tag can override this..
4829 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4832 if (this.brand !== '') {
4833 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4834 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4836 href: this.brand_href ? this.brand_href : '#',
4837 cls: 'navbar-brand',
4845 cfg.cls += ' main-nav';
4853 getHeaderChildContainer : function()
4855 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4856 return this.el.select('.navbar-header',true).first();
4859 return this.getChildContainer();
4862 getChildContainer : function()
4865 return this.el.select('.roo-navbar-collapse',true).first();
4870 initEvents : function()
4872 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4874 if (this.autohide) {
4879 Roo.get(document).on('scroll',function(e) {
4880 var ns = Roo.get(document).getScroll().top;
4881 var os = prevScroll;
4885 ft.removeClass('slideDown');
4886 ft.addClass('slideUp');
4889 ft.removeClass('slideUp');
4890 ft.addClass('slideDown');
4911 * @class Roo.bootstrap.NavSidebar
4912 * @extends Roo.bootstrap.Navbar
4913 * Bootstrap Sidebar class
4916 * Create a new Sidebar
4917 * @param {Object} config The config object
4921 Roo.bootstrap.NavSidebar = function(config){
4922 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4925 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4927 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4929 getAutoCreate : function(){
4934 cls: 'sidebar sidebar-nav'
4956 * @class Roo.bootstrap.NavGroup
4957 * @extends Roo.bootstrap.Component
4958 * Bootstrap NavGroup class
4959 * @cfg {String} align (left|right)
4960 * @cfg {Boolean} inverse
4961 * @cfg {String} type (nav|pills|tab) default nav
4962 * @cfg {String} navId - reference Id for navbar.
4966 * Create a new nav group
4967 * @param {Object} config The config object
4970 Roo.bootstrap.NavGroup = function(config){
4971 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4974 Roo.bootstrap.NavGroup.register(this);
4978 * Fires when the active item changes
4979 * @param {Roo.bootstrap.NavGroup} this
4980 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4981 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4988 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4999 getAutoCreate : function()
5001 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5007 if (Roo.bootstrap.version == 4) {
5008 if (['tabs','pills'].indexOf(this.type) != -1) {
5009 cfg.cls += ' nav-' + this.type;
5011 // trying to remove so header bar can right align top?
5012 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5013 // do not use on header bar...
5014 cfg.cls += ' navbar-nav';
5019 if (['tabs','pills'].indexOf(this.type) != -1) {
5020 cfg.cls += ' nav-' + this.type
5022 if (this.type !== 'nav') {
5023 Roo.log('nav type must be nav/tabs/pills')
5025 cfg.cls += ' navbar-nav'
5029 if (this.parent() && this.parent().sidebar) {
5032 cls: 'dashboard-menu sidebar-menu'
5038 if (this.form === true) {
5041 cls: 'navbar-form form-inline'
5043 //nav navbar-right ml-md-auto
5044 if (this.align === 'right') {
5045 cfg.cls += ' navbar-right ml-md-auto';
5047 cfg.cls += ' navbar-left';
5051 if (this.align === 'right') {
5052 cfg.cls += ' navbar-right ml-md-auto';
5054 cfg.cls += ' mr-auto';
5058 cfg.cls += ' navbar-inverse';
5066 * sets the active Navigation item
5067 * @param {Roo.bootstrap.NavItem} the new current navitem
5069 setActiveItem : function(item)
5072 Roo.each(this.navItems, function(v){
5077 v.setActive(false, true);
5084 item.setActive(true, true);
5085 this.fireEvent('changed', this, item, prev);
5090 * gets the active Navigation item
5091 * @return {Roo.bootstrap.NavItem} the current navitem
5093 getActive : function()
5097 Roo.each(this.navItems, function(v){
5108 indexOfNav : function()
5112 Roo.each(this.navItems, function(v,i){
5123 * adds a Navigation item
5124 * @param {Roo.bootstrap.NavItem} the navitem to add
5126 addItem : function(cfg)
5128 if (this.form && Roo.bootstrap.version == 4) {
5131 var cn = new Roo.bootstrap.NavItem(cfg);
5133 cn.parentId = this.id;
5134 cn.onRender(this.el, null);
5138 * register a Navigation item
5139 * @param {Roo.bootstrap.NavItem} the navitem to add
5141 register : function(item)
5143 this.navItems.push( item);
5144 item.navId = this.navId;
5149 * clear all the Navigation item
5152 clearAll : function()
5155 this.el.dom.innerHTML = '';
5158 getNavItem: function(tabId)
5161 Roo.each(this.navItems, function(e) {
5162 if (e.tabId == tabId) {
5172 setActiveNext : function()
5174 var i = this.indexOfNav(this.getActive());
5175 if (i > this.navItems.length) {
5178 this.setActiveItem(this.navItems[i+1]);
5180 setActivePrev : function()
5182 var i = this.indexOfNav(this.getActive());
5186 this.setActiveItem(this.navItems[i-1]);
5188 clearWasActive : function(except) {
5189 Roo.each(this.navItems, function(e) {
5190 if (e.tabId != except.tabId && e.was_active) {
5191 e.was_active = false;
5198 getWasActive : function ()
5201 Roo.each(this.navItems, function(e) {
5216 Roo.apply(Roo.bootstrap.NavGroup, {
5220 * register a Navigation Group
5221 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5223 register : function(navgrp)
5225 this.groups[navgrp.navId] = navgrp;
5229 * fetch a Navigation Group based on the navigation ID
5230 * @param {string} the navgroup to add
5231 * @returns {Roo.bootstrap.NavGroup} the navgroup
5233 get: function(navId) {
5234 if (typeof(this.groups[navId]) == 'undefined') {
5236 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5238 return this.groups[navId] ;
5253 * @class Roo.bootstrap.NavItem
5254 * @extends Roo.bootstrap.Component
5255 * Bootstrap Navbar.NavItem class
5256 * @cfg {String} href link to
5257 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5259 * @cfg {String} html content of button
5260 * @cfg {String} badge text inside badge
5261 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5262 * @cfg {String} glyphicon DEPRICATED - use fa
5263 * @cfg {String} icon DEPRICATED - use fa
5264 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5265 * @cfg {Boolean} active Is item active
5266 * @cfg {Boolean} disabled Is item disabled
5268 * @cfg {Boolean} preventDefault (true | false) default false
5269 * @cfg {String} tabId the tab that this item activates.
5270 * @cfg {String} tagtype (a|span) render as a href or span?
5271 * @cfg {Boolean} animateRef (true|false) link to element default false
5274 * Create a new Navbar Item
5275 * @param {Object} config The config object
5277 Roo.bootstrap.NavItem = function(config){
5278 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5283 * The raw click event for the entire grid.
5284 * @param {Roo.EventObject} e
5289 * Fires when the active item active state changes
5290 * @param {Roo.bootstrap.NavItem} this
5291 * @param {boolean} state the new state
5297 * Fires when scroll to element
5298 * @param {Roo.bootstrap.NavItem} this
5299 * @param {Object} options
5300 * @param {Roo.EventObject} e
5308 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5317 preventDefault : false,
5325 button_outline : false,
5329 getAutoCreate : function(){
5337 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5339 if (this.disabled) {
5340 cfg.cls += ' disabled';
5344 if (this.button_weight.length) {
5345 cfg.tag = this.href ? 'a' : 'button';
5346 cfg.html = this.html || '';
5347 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5349 cfg.href = this.href;
5352 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5355 // menu .. should add dropdown-menu class - so no need for carat..
5357 if (this.badge !== '') {
5359 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5364 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5368 href : this.href || "#",
5369 html: this.html || ''
5372 if (this.tagtype == 'a') {
5373 cfg.cn[0].cls = 'nav-link';
5376 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5379 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5381 if(this.glyphicon) {
5382 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5387 cfg.cn[0].html += " <span class='caret'></span>";
5391 if (this.badge !== '') {
5393 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5401 onRender : function(ct, position)
5403 // Roo.log("Call onRender: " + this.xtype);
5404 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5408 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5409 this.navLink = this.el.select('.nav-link',true).first();
5414 initEvents: function()
5416 if (typeof (this.menu) != 'undefined') {
5417 this.menu.parentType = this.xtype;
5418 this.menu.triggerEl = this.el;
5419 this.menu = this.addxtype(Roo.apply({}, this.menu));
5422 this.el.select('a',true).on('click', this.onClick, this);
5424 if(this.tagtype == 'span'){
5425 this.el.select('span',true).on('click', this.onClick, this);
5428 // at this point parent should be available..
5429 this.parent().register(this);
5432 onClick : function(e)
5434 if (e.getTarget('.dropdown-menu-item')) {
5435 // did you click on a menu itemm.... - then don't trigger onclick..
5440 this.preventDefault ||
5443 Roo.log("NavItem - prevent Default?");
5447 if (this.disabled) {
5451 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5452 if (tg && tg.transition) {
5453 Roo.log("waiting for the transitionend");
5459 //Roo.log("fire event clicked");
5460 if(this.fireEvent('click', this, e) === false){
5464 if(this.tagtype == 'span'){
5468 //Roo.log(this.href);
5469 var ael = this.el.select('a',true).first();
5472 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5473 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5474 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5475 return; // ignore... - it's a 'hash' to another page.
5477 Roo.log("NavItem - prevent Default?");
5479 this.scrollToElement(e);
5483 var p = this.parent();
5485 if (['tabs','pills'].indexOf(p.type)!==-1) {
5486 if (typeof(p.setActiveItem) !== 'undefined') {
5487 p.setActiveItem(this);
5491 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5492 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5493 // remove the collapsed menu expand...
5494 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5498 isActive: function () {
5501 setActive : function(state, fire, is_was_active)
5503 if (this.active && !state && this.navId) {
5504 this.was_active = true;
5505 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5507 nv.clearWasActive(this);
5511 this.active = state;
5514 this.el.removeClass('active');
5515 this.navLink ? this.navLink.removeClass('active') : false;
5516 } else if (!this.el.hasClass('active')) {
5518 this.el.addClass('active');
5519 if (Roo.bootstrap.version == 4 && this.navLink ) {
5520 this.navLink.addClass('active');
5525 this.fireEvent('changed', this, state);
5528 // show a panel if it's registered and related..
5530 if (!this.navId || !this.tabId || !state || is_was_active) {
5534 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5538 var pan = tg.getPanelByName(this.tabId);
5542 // if we can not flip to new panel - go back to old nav highlight..
5543 if (false == tg.showPanel(pan)) {
5544 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5546 var onav = nv.getWasActive();
5548 onav.setActive(true, false, true);
5557 // this should not be here...
5558 setDisabled : function(state)
5560 this.disabled = state;
5562 this.el.removeClass('disabled');
5563 } else if (!this.el.hasClass('disabled')) {
5564 this.el.addClass('disabled');
5570 * Fetch the element to display the tooltip on.
5571 * @return {Roo.Element} defaults to this.el
5573 tooltipEl : function()
5575 return this.el.select('' + this.tagtype + '', true).first();
5578 scrollToElement : function(e)
5580 var c = document.body;
5583 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5585 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5586 c = document.documentElement;
5589 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5595 var o = target.calcOffsetsTo(c);
5602 this.fireEvent('scrollto', this, options, e);
5604 Roo.get(c).scrollTo('top', options.value, true);
5617 * <span> icon </span>
5618 * <span> text </span>
5619 * <span>badge </span>
5623 * @class Roo.bootstrap.NavSidebarItem
5624 * @extends Roo.bootstrap.NavItem
5625 * Bootstrap Navbar.NavSidebarItem class
5626 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5627 * {Boolean} open is the menu open
5628 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5629 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5630 * {String} buttonSize (sm|md|lg)the extra classes for the button
5631 * {Boolean} showArrow show arrow next to the text (default true)
5633 * Create a new Navbar Button
5634 * @param {Object} config The config object
5636 Roo.bootstrap.NavSidebarItem = function(config){
5637 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5642 * The raw click event for the entire grid.
5643 * @param {Roo.EventObject} e
5648 * Fires when the active item active state changes
5649 * @param {Roo.bootstrap.NavSidebarItem} this
5650 * @param {boolean} state the new state
5658 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5660 badgeWeight : 'default',
5666 buttonWeight : 'default',
5672 getAutoCreate : function(){
5677 href : this.href || '#',
5683 if(this.buttonView){
5686 href : this.href || '#',
5687 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5700 cfg.cls += ' active';
5703 if (this.disabled) {
5704 cfg.cls += ' disabled';
5707 cfg.cls += ' open x-open';
5710 if (this.glyphicon || this.icon) {
5711 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5712 a.cn.push({ tag : 'i', cls : c }) ;
5715 if(!this.buttonView){
5718 html : this.html || ''
5725 if (this.badge !== '') {
5726 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5732 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5735 a.cls += ' dropdown-toggle treeview' ;
5741 initEvents : function()
5743 if (typeof (this.menu) != 'undefined') {
5744 this.menu.parentType = this.xtype;
5745 this.menu.triggerEl = this.el;
5746 this.menu = this.addxtype(Roo.apply({}, this.menu));
5749 this.el.on('click', this.onClick, this);
5751 if(this.badge !== ''){
5752 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5757 onClick : function(e)
5764 if(this.preventDefault){
5768 this.fireEvent('click', this, e);
5771 disable : function()
5773 this.setDisabled(true);
5778 this.setDisabled(false);
5781 setDisabled : function(state)
5783 if(this.disabled == state){
5787 this.disabled = state;
5790 this.el.addClass('disabled');
5794 this.el.removeClass('disabled');
5799 setActive : function(state)
5801 if(this.active == state){
5805 this.active = state;
5808 this.el.addClass('active');
5812 this.el.removeClass('active');
5817 isActive: function ()
5822 setBadge : function(str)
5828 this.badgeEl.dom.innerHTML = str;
5845 * @class Roo.bootstrap.Row
5846 * @extends Roo.bootstrap.Component
5847 * Bootstrap Row class (contains columns...)
5851 * @param {Object} config The config object
5854 Roo.bootstrap.Row = function(config){
5855 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5858 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5860 getAutoCreate : function(){
5879 * @class Roo.bootstrap.Pagination
5880 * @extends Roo.bootstrap.Component
5881 * Bootstrap Pagination class
5882 * @cfg {String} size xs | sm | md | lg
5883 * @cfg {Boolean} inverse false | true
5886 * Create a new Pagination
5887 * @param {Object} config The config object
5890 Roo.bootstrap.Pagination = function(config){
5891 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5894 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5900 getAutoCreate : function(){
5906 cfg.cls += ' inverse';
5912 cfg.cls += " " + this.cls;
5930 * @class Roo.bootstrap.PaginationItem
5931 * @extends Roo.bootstrap.Component
5932 * Bootstrap PaginationItem class
5933 * @cfg {String} html text
5934 * @cfg {String} href the link
5935 * @cfg {Boolean} preventDefault (true | false) default true
5936 * @cfg {Boolean} active (true | false) default false
5937 * @cfg {Boolean} disabled default false
5941 * Create a new PaginationItem
5942 * @param {Object} config The config object
5946 Roo.bootstrap.PaginationItem = function(config){
5947 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5952 * The raw click event for the entire grid.
5953 * @param {Roo.EventObject} e
5959 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5963 preventDefault: true,
5968 getAutoCreate : function(){
5974 href : this.href ? this.href : '#',
5975 html : this.html ? this.html : ''
5985 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5989 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5995 initEvents: function() {
5997 this.el.on('click', this.onClick, this);
6000 onClick : function(e)
6002 Roo.log('PaginationItem on click ');
6003 if(this.preventDefault){
6011 this.fireEvent('click', this, e);
6027 * @class Roo.bootstrap.Slider
6028 * @extends Roo.bootstrap.Component
6029 * Bootstrap Slider class
6032 * Create a new Slider
6033 * @param {Object} config The config object
6036 Roo.bootstrap.Slider = function(config){
6037 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6040 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6042 getAutoCreate : function(){
6046 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6050 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6062 * Ext JS Library 1.1.1
6063 * Copyright(c) 2006-2007, Ext JS, LLC.
6065 * Originally Released Under LGPL - original licence link has changed is not relivant.
6068 * <script type="text/javascript">
6073 * @class Roo.grid.ColumnModel
6074 * @extends Roo.util.Observable
6075 * This is the default implementation of a ColumnModel used by the Grid. It defines
6076 * the columns in the grid.
6079 var colModel = new Roo.grid.ColumnModel([
6080 {header: "Ticker", width: 60, sortable: true, locked: true},
6081 {header: "Company Name", width: 150, sortable: true},
6082 {header: "Market Cap.", width: 100, sortable: true},
6083 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6084 {header: "Employees", width: 100, sortable: true, resizable: false}
6089 * The config options listed for this class are options which may appear in each
6090 * individual column definition.
6091 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6093 * @param {Object} config An Array of column config objects. See this class's
6094 * config objects for details.
6096 Roo.grid.ColumnModel = function(config){
6098 * The config passed into the constructor
6100 this.config = config;
6103 // if no id, create one
6104 // if the column does not have a dataIndex mapping,
6105 // map it to the order it is in the config
6106 for(var i = 0, len = config.length; i < len; i++){
6108 if(typeof c.dataIndex == "undefined"){
6111 if(typeof c.renderer == "string"){
6112 c.renderer = Roo.util.Format[c.renderer];
6114 if(typeof c.id == "undefined"){
6117 if(c.editor && c.editor.xtype){
6118 c.editor = Roo.factory(c.editor, Roo.grid);
6120 if(c.editor && c.editor.isFormField){
6121 c.editor = new Roo.grid.GridEditor(c.editor);
6123 this.lookup[c.id] = c;
6127 * The width of columns which have no width specified (defaults to 100)
6130 this.defaultWidth = 100;
6133 * Default sortable of columns which have no sortable specified (defaults to false)
6136 this.defaultSortable = false;
6140 * @event widthchange
6141 * Fires when the width of a column changes.
6142 * @param {ColumnModel} this
6143 * @param {Number} columnIndex The column index
6144 * @param {Number} newWidth The new width
6146 "widthchange": true,
6148 * @event headerchange
6149 * Fires when the text of a header changes.
6150 * @param {ColumnModel} this
6151 * @param {Number} columnIndex The column index
6152 * @param {Number} newText The new header text
6154 "headerchange": true,
6156 * @event hiddenchange
6157 * Fires when a column is hidden or "unhidden".
6158 * @param {ColumnModel} this
6159 * @param {Number} columnIndex The column index
6160 * @param {Boolean} hidden true if hidden, false otherwise
6162 "hiddenchange": true,
6164 * @event columnmoved
6165 * Fires when a column is moved.
6166 * @param {ColumnModel} this
6167 * @param {Number} oldIndex
6168 * @param {Number} newIndex
6170 "columnmoved" : true,
6172 * @event columlockchange
6173 * Fires when a column's locked state is changed
6174 * @param {ColumnModel} this
6175 * @param {Number} colIndex
6176 * @param {Boolean} locked true if locked
6178 "columnlockchange" : true
6180 Roo.grid.ColumnModel.superclass.constructor.call(this);
6182 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6184 * @cfg {String} header The header text to display in the Grid view.
6187 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6188 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6189 * specified, the column's index is used as an index into the Record's data Array.
6192 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6193 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6196 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6197 * Defaults to the value of the {@link #defaultSortable} property.
6198 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6201 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6204 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6207 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6210 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6213 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6214 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6215 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6216 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6219 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6222 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6225 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6228 * @cfg {String} cursor (Optional)
6231 * @cfg {String} tooltip (Optional)
6234 * @cfg {Number} xs (Optional)
6237 * @cfg {Number} sm (Optional)
6240 * @cfg {Number} md (Optional)
6243 * @cfg {Number} lg (Optional)
6246 * Returns the id of the column at the specified index.
6247 * @param {Number} index The column index
6248 * @return {String} the id
6250 getColumnId : function(index){
6251 return this.config[index].id;
6255 * Returns the column for a specified id.
6256 * @param {String} id The column id
6257 * @return {Object} the column
6259 getColumnById : function(id){
6260 return this.lookup[id];
6265 * Returns the column for a specified dataIndex.
6266 * @param {String} dataIndex The column dataIndex
6267 * @return {Object|Boolean} the column or false if not found
6269 getColumnByDataIndex: function(dataIndex){
6270 var index = this.findColumnIndex(dataIndex);
6271 return index > -1 ? this.config[index] : false;
6275 * Returns the index for a specified column id.
6276 * @param {String} id The column id
6277 * @return {Number} the index, or -1 if not found
6279 getIndexById : function(id){
6280 for(var i = 0, len = this.config.length; i < len; i++){
6281 if(this.config[i].id == id){
6289 * Returns the index for a specified column dataIndex.
6290 * @param {String} dataIndex The column dataIndex
6291 * @return {Number} the index, or -1 if not found
6294 findColumnIndex : function(dataIndex){
6295 for(var i = 0, len = this.config.length; i < len; i++){
6296 if(this.config[i].dataIndex == dataIndex){
6304 moveColumn : function(oldIndex, newIndex){
6305 var c = this.config[oldIndex];
6306 this.config.splice(oldIndex, 1);
6307 this.config.splice(newIndex, 0, c);
6308 this.dataMap = null;
6309 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6312 isLocked : function(colIndex){
6313 return this.config[colIndex].locked === true;
6316 setLocked : function(colIndex, value, suppressEvent){
6317 if(this.isLocked(colIndex) == value){
6320 this.config[colIndex].locked = value;
6322 this.fireEvent("columnlockchange", this, colIndex, value);
6326 getTotalLockedWidth : function(){
6328 for(var i = 0; i < this.config.length; i++){
6329 if(this.isLocked(i) && !this.isHidden(i)){
6330 this.totalWidth += this.getColumnWidth(i);
6336 getLockedCount : function(){
6337 for(var i = 0, len = this.config.length; i < len; i++){
6338 if(!this.isLocked(i)){
6343 return this.config.length;
6347 * Returns the number of columns.
6350 getColumnCount : function(visibleOnly){
6351 if(visibleOnly === true){
6353 for(var i = 0, len = this.config.length; i < len; i++){
6354 if(!this.isHidden(i)){
6360 return this.config.length;
6364 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6365 * @param {Function} fn
6366 * @param {Object} scope (optional)
6367 * @return {Array} result
6369 getColumnsBy : function(fn, scope){
6371 for(var i = 0, len = this.config.length; i < len; i++){
6372 var c = this.config[i];
6373 if(fn.call(scope||this, c, i) === true){
6381 * Returns true if the specified column is sortable.
6382 * @param {Number} col The column index
6385 isSortable : function(col){
6386 if(typeof this.config[col].sortable == "undefined"){
6387 return this.defaultSortable;
6389 return this.config[col].sortable;
6393 * Returns the rendering (formatting) function defined for the column.
6394 * @param {Number} col The column index.
6395 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6397 getRenderer : function(col){
6398 if(!this.config[col].renderer){
6399 return Roo.grid.ColumnModel.defaultRenderer;
6401 return this.config[col].renderer;
6405 * Sets the rendering (formatting) function for a column.
6406 * @param {Number} col The column index
6407 * @param {Function} fn The function to use to process the cell's raw data
6408 * to return HTML markup for the grid view. The render function is called with
6409 * the following parameters:<ul>
6410 * <li>Data value.</li>
6411 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6412 * <li>css A CSS style string to apply to the table cell.</li>
6413 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6414 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6415 * <li>Row index</li>
6416 * <li>Column index</li>
6417 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6419 setRenderer : function(col, fn){
6420 this.config[col].renderer = fn;
6424 * Returns the width for the specified column.
6425 * @param {Number} col The column index
6428 getColumnWidth : function(col){
6429 return this.config[col].width * 1 || this.defaultWidth;
6433 * Sets the width for a column.
6434 * @param {Number} col The column index
6435 * @param {Number} width The new width
6437 setColumnWidth : function(col, width, suppressEvent){
6438 this.config[col].width = width;
6439 this.totalWidth = null;
6441 this.fireEvent("widthchange", this, col, width);
6446 * Returns the total width of all columns.
6447 * @param {Boolean} includeHidden True to include hidden column widths
6450 getTotalWidth : function(includeHidden){
6451 if(!this.totalWidth){
6452 this.totalWidth = 0;
6453 for(var i = 0, len = this.config.length; i < len; i++){
6454 if(includeHidden || !this.isHidden(i)){
6455 this.totalWidth += this.getColumnWidth(i);
6459 return this.totalWidth;
6463 * Returns the header for the specified column.
6464 * @param {Number} col The column index
6467 getColumnHeader : function(col){
6468 return this.config[col].header;
6472 * Sets the header for a column.
6473 * @param {Number} col The column index
6474 * @param {String} header The new header
6476 setColumnHeader : function(col, header){
6477 this.config[col].header = header;
6478 this.fireEvent("headerchange", this, col, header);
6482 * Returns the tooltip for the specified column.
6483 * @param {Number} col The column index
6486 getColumnTooltip : function(col){
6487 return this.config[col].tooltip;
6490 * Sets the tooltip for a column.
6491 * @param {Number} col The column index
6492 * @param {String} tooltip The new tooltip
6494 setColumnTooltip : function(col, tooltip){
6495 this.config[col].tooltip = tooltip;
6499 * Returns the dataIndex for the specified column.
6500 * @param {Number} col The column index
6503 getDataIndex : function(col){
6504 return this.config[col].dataIndex;
6508 * Sets the dataIndex for a column.
6509 * @param {Number} col The column index
6510 * @param {Number} dataIndex The new dataIndex
6512 setDataIndex : function(col, dataIndex){
6513 this.config[col].dataIndex = dataIndex;
6519 * Returns true if the cell is editable.
6520 * @param {Number} colIndex The column index
6521 * @param {Number} rowIndex The row index - this is nto actually used..?
6524 isCellEditable : function(colIndex, rowIndex){
6525 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6529 * Returns the editor defined for the cell/column.
6530 * return false or null to disable editing.
6531 * @param {Number} colIndex The column index
6532 * @param {Number} rowIndex The row index
6535 getCellEditor : function(colIndex, rowIndex){
6536 return this.config[colIndex].editor;
6540 * Sets if a column is editable.
6541 * @param {Number} col The column index
6542 * @param {Boolean} editable True if the column is editable
6544 setEditable : function(col, editable){
6545 this.config[col].editable = editable;
6550 * Returns true if the column is hidden.
6551 * @param {Number} colIndex The column index
6554 isHidden : function(colIndex){
6555 return this.config[colIndex].hidden;
6560 * Returns true if the column width cannot be changed
6562 isFixed : function(colIndex){
6563 return this.config[colIndex].fixed;
6567 * Returns true if the column can be resized
6570 isResizable : function(colIndex){
6571 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6574 * Sets if a column is hidden.
6575 * @param {Number} colIndex The column index
6576 * @param {Boolean} hidden True if the column is hidden
6578 setHidden : function(colIndex, hidden){
6579 this.config[colIndex].hidden = hidden;
6580 this.totalWidth = null;
6581 this.fireEvent("hiddenchange", this, colIndex, hidden);
6585 * Sets the editor for a column.
6586 * @param {Number} col The column index
6587 * @param {Object} editor The editor object
6589 setEditor : function(col, editor){
6590 this.config[col].editor = editor;
6594 Roo.grid.ColumnModel.defaultRenderer = function(value)
6596 if(typeof value == "object") {
6599 if(typeof value == "string" && value.length < 1){
6603 return String.format("{0}", value);
6606 // Alias for backwards compatibility
6607 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6610 * Ext JS Library 1.1.1
6611 * Copyright(c) 2006-2007, Ext JS, LLC.
6613 * Originally Released Under LGPL - original licence link has changed is not relivant.
6616 * <script type="text/javascript">
6620 * @class Roo.LoadMask
6621 * A simple utility class for generically masking elements while loading data. If the element being masked has
6622 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6623 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6624 * element's UpdateManager load indicator and will be destroyed after the initial load.
6626 * Create a new LoadMask
6627 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6628 * @param {Object} config The config object
6630 Roo.LoadMask = function(el, config){
6631 this.el = Roo.get(el);
6632 Roo.apply(this, config);
6634 this.store.on('beforeload', this.onBeforeLoad, this);
6635 this.store.on('load', this.onLoad, this);
6636 this.store.on('loadexception', this.onLoadException, this);
6637 this.removeMask = false;
6639 var um = this.el.getUpdateManager();
6640 um.showLoadIndicator = false; // disable the default indicator
6641 um.on('beforeupdate', this.onBeforeLoad, this);
6642 um.on('update', this.onLoad, this);
6643 um.on('failure', this.onLoad, this);
6644 this.removeMask = true;
6648 Roo.LoadMask.prototype = {
6650 * @cfg {Boolean} removeMask
6651 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6652 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6656 * The text to display in a centered loading message box (defaults to 'Loading...')
6660 * @cfg {String} msgCls
6661 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6663 msgCls : 'x-mask-loading',
6666 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6672 * Disables the mask to prevent it from being displayed
6674 disable : function(){
6675 this.disabled = true;
6679 * Enables the mask so that it can be displayed
6681 enable : function(){
6682 this.disabled = false;
6685 onLoadException : function()
6689 if (typeof(arguments[3]) != 'undefined') {
6690 Roo.MessageBox.alert("Error loading",arguments[3]);
6694 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6695 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6702 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6707 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6711 onBeforeLoad : function(){
6713 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6718 destroy : function(){
6720 this.store.un('beforeload', this.onBeforeLoad, this);
6721 this.store.un('load', this.onLoad, this);
6722 this.store.un('loadexception', this.onLoadException, this);
6724 var um = this.el.getUpdateManager();
6725 um.un('beforeupdate', this.onBeforeLoad, this);
6726 um.un('update', this.onLoad, this);
6727 um.un('failure', this.onLoad, this);
6738 * @class Roo.bootstrap.Table
6739 * @extends Roo.bootstrap.Component
6740 * Bootstrap Table class
6741 * @cfg {String} cls table class
6742 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6743 * @cfg {String} bgcolor Specifies the background color for a table
6744 * @cfg {Number} border Specifies whether the table cells should have borders or not
6745 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6746 * @cfg {Number} cellspacing Specifies the space between cells
6747 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6748 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6749 * @cfg {String} sortable Specifies that the table should be sortable
6750 * @cfg {String} summary Specifies a summary of the content of a table
6751 * @cfg {Number} width Specifies the width of a table
6752 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6754 * @cfg {boolean} striped Should the rows be alternative striped
6755 * @cfg {boolean} bordered Add borders to the table
6756 * @cfg {boolean} hover Add hover highlighting
6757 * @cfg {boolean} condensed Format condensed
6758 * @cfg {boolean} responsive Format condensed
6759 * @cfg {Boolean} loadMask (true|false) default false
6760 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6761 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6762 * @cfg {Boolean} rowSelection (true|false) default false
6763 * @cfg {Boolean} cellSelection (true|false) default false
6764 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6765 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6766 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6767 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6771 * Create a new Table
6772 * @param {Object} config The config object
6775 Roo.bootstrap.Table = function(config){
6776 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6781 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6782 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6783 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6784 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6786 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6788 this.sm.grid = this;
6789 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6790 this.sm = this.selModel;
6791 this.sm.xmodule = this.xmodule || false;
6794 if (this.cm && typeof(this.cm.config) == 'undefined') {
6795 this.colModel = new Roo.grid.ColumnModel(this.cm);
6796 this.cm = this.colModel;
6797 this.cm.xmodule = this.xmodule || false;
6800 this.store= Roo.factory(this.store, Roo.data);
6801 this.ds = this.store;
6802 this.ds.xmodule = this.xmodule || false;
6805 if (this.footer && this.store) {
6806 this.footer.dataSource = this.ds;
6807 this.footer = Roo.factory(this.footer);
6814 * Fires when a cell is clicked
6815 * @param {Roo.bootstrap.Table} this
6816 * @param {Roo.Element} el
6817 * @param {Number} rowIndex
6818 * @param {Number} columnIndex
6819 * @param {Roo.EventObject} e
6823 * @event celldblclick
6824 * Fires when a cell is double clicked
6825 * @param {Roo.bootstrap.Table} this
6826 * @param {Roo.Element} el
6827 * @param {Number} rowIndex
6828 * @param {Number} columnIndex
6829 * @param {Roo.EventObject} e
6831 "celldblclick" : true,
6834 * Fires when a row is clicked
6835 * @param {Roo.bootstrap.Table} this
6836 * @param {Roo.Element} el
6837 * @param {Number} rowIndex
6838 * @param {Roo.EventObject} e
6842 * @event rowdblclick
6843 * Fires when a row is double clicked
6844 * @param {Roo.bootstrap.Table} this
6845 * @param {Roo.Element} el
6846 * @param {Number} rowIndex
6847 * @param {Roo.EventObject} e
6849 "rowdblclick" : true,
6852 * Fires when a mouseover occur
6853 * @param {Roo.bootstrap.Table} this
6854 * @param {Roo.Element} el
6855 * @param {Number} rowIndex
6856 * @param {Number} columnIndex
6857 * @param {Roo.EventObject} e
6862 * Fires when a mouseout occur
6863 * @param {Roo.bootstrap.Table} this
6864 * @param {Roo.Element} el
6865 * @param {Number} rowIndex
6866 * @param {Number} columnIndex
6867 * @param {Roo.EventObject} e
6872 * Fires when a row is rendered, so you can change add a style to it.
6873 * @param {Roo.bootstrap.Table} this
6874 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6878 * @event rowsrendered
6879 * Fires when all the rows have been rendered
6880 * @param {Roo.bootstrap.Table} this
6882 'rowsrendered' : true,
6884 * @event contextmenu
6885 * The raw contextmenu event for the entire grid.
6886 * @param {Roo.EventObject} e
6888 "contextmenu" : true,
6890 * @event rowcontextmenu
6891 * Fires when a row is right clicked
6892 * @param {Roo.bootstrap.Table} this
6893 * @param {Number} rowIndex
6894 * @param {Roo.EventObject} e
6896 "rowcontextmenu" : true,
6898 * @event cellcontextmenu
6899 * Fires when a cell is right clicked
6900 * @param {Roo.bootstrap.Table} this
6901 * @param {Number} rowIndex
6902 * @param {Number} cellIndex
6903 * @param {Roo.EventObject} e
6905 "cellcontextmenu" : true,
6907 * @event headercontextmenu
6908 * Fires when a header is right clicked
6909 * @param {Roo.bootstrap.Table} this
6910 * @param {Number} columnIndex
6911 * @param {Roo.EventObject} e
6913 "headercontextmenu" : true
6917 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6943 rowSelection : false,
6944 cellSelection : false,
6947 // Roo.Element - the tbody
6949 // Roo.Element - thead element
6952 container: false, // used by gridpanel...
6958 auto_hide_footer : false,
6960 getAutoCreate : function()
6962 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6969 if (this.scrollBody) {
6970 cfg.cls += ' table-body-fixed';
6973 cfg.cls += ' table-striped';
6977 cfg.cls += ' table-hover';
6979 if (this.bordered) {
6980 cfg.cls += ' table-bordered';
6982 if (this.condensed) {
6983 cfg.cls += ' table-condensed';
6985 if (this.responsive) {
6986 cfg.cls += ' table-responsive';
6990 cfg.cls+= ' ' +this.cls;
6993 // this lot should be simplifed...
7006 ].forEach(function(k) {
7014 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7017 if(this.store || this.cm){
7018 if(this.headerShow){
7019 cfg.cn.push(this.renderHeader());
7022 cfg.cn.push(this.renderBody());
7024 if(this.footerShow){
7025 cfg.cn.push(this.renderFooter());
7027 // where does this come from?
7028 //cfg.cls+= ' TableGrid';
7031 return { cn : [ cfg ] };
7034 initEvents : function()
7036 if(!this.store || !this.cm){
7039 if (this.selModel) {
7040 this.selModel.initEvents();
7044 //Roo.log('initEvents with ds!!!!');
7046 this.mainBody = this.el.select('tbody', true).first();
7047 this.mainHead = this.el.select('thead', true).first();
7048 this.mainFoot = this.el.select('tfoot', true).first();
7054 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7055 e.on('click', _this.sort, _this);
7058 this.mainBody.on("click", this.onClick, this);
7059 this.mainBody.on("dblclick", this.onDblClick, this);
7061 // why is this done????? = it breaks dialogs??
7062 //this.parent().el.setStyle('position', 'relative');
7066 this.footer.parentId = this.id;
7067 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7070 this.el.select('tfoot tr td').first().addClass('hide');
7075 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7078 this.store.on('load', this.onLoad, this);
7079 this.store.on('beforeload', this.onBeforeLoad, this);
7080 this.store.on('update', this.onUpdate, this);
7081 this.store.on('add', this.onAdd, this);
7082 this.store.on("clear", this.clear, this);
7084 this.el.on("contextmenu", this.onContextMenu, this);
7086 this.mainBody.on('scroll', this.onBodyScroll, this);
7088 this.cm.on("headerchange", this.onHeaderChange, this);
7090 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7094 onContextMenu : function(e, t)
7096 this.processEvent("contextmenu", e);
7099 processEvent : function(name, e)
7101 if (name != 'touchstart' ) {
7102 this.fireEvent(name, e);
7105 var t = e.getTarget();
7107 var cell = Roo.get(t);
7113 if(cell.findParent('tfoot', false, true)){
7117 if(cell.findParent('thead', false, true)){
7119 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7120 cell = Roo.get(t).findParent('th', false, true);
7122 Roo.log("failed to find th in thead?");
7123 Roo.log(e.getTarget());
7128 var cellIndex = cell.dom.cellIndex;
7130 var ename = name == 'touchstart' ? 'click' : name;
7131 this.fireEvent("header" + ename, this, cellIndex, e);
7136 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7137 cell = Roo.get(t).findParent('td', false, true);
7139 Roo.log("failed to find th in tbody?");
7140 Roo.log(e.getTarget());
7145 var row = cell.findParent('tr', false, true);
7146 var cellIndex = cell.dom.cellIndex;
7147 var rowIndex = row.dom.rowIndex - 1;
7151 this.fireEvent("row" + name, this, rowIndex, e);
7155 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7161 onMouseover : function(e, el)
7163 var cell = Roo.get(el);
7169 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7170 cell = cell.findParent('td', false, true);
7173 var row = cell.findParent('tr', false, true);
7174 var cellIndex = cell.dom.cellIndex;
7175 var rowIndex = row.dom.rowIndex - 1; // start from 0
7177 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7181 onMouseout : function(e, el)
7183 var cell = Roo.get(el);
7189 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7190 cell = cell.findParent('td', false, true);
7193 var row = cell.findParent('tr', false, true);
7194 var cellIndex = cell.dom.cellIndex;
7195 var rowIndex = row.dom.rowIndex - 1; // start from 0
7197 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7201 onClick : function(e, el)
7203 var cell = Roo.get(el);
7205 if(!cell || (!this.cellSelection && !this.rowSelection)){
7209 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7210 cell = cell.findParent('td', false, true);
7213 if(!cell || typeof(cell) == 'undefined'){
7217 var row = cell.findParent('tr', false, true);
7219 if(!row || typeof(row) == 'undefined'){
7223 var cellIndex = cell.dom.cellIndex;
7224 var rowIndex = this.getRowIndex(row);
7226 // why??? - should these not be based on SelectionModel?
7227 if(this.cellSelection){
7228 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7231 if(this.rowSelection){
7232 this.fireEvent('rowclick', this, row, rowIndex, e);
7238 onDblClick : function(e,el)
7240 var cell = Roo.get(el);
7242 if(!cell || (!this.cellSelection && !this.rowSelection)){
7246 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7247 cell = cell.findParent('td', false, true);
7250 if(!cell || typeof(cell) == 'undefined'){
7254 var row = cell.findParent('tr', false, true);
7256 if(!row || typeof(row) == 'undefined'){
7260 var cellIndex = cell.dom.cellIndex;
7261 var rowIndex = this.getRowIndex(row);
7263 if(this.cellSelection){
7264 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7267 if(this.rowSelection){
7268 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7272 sort : function(e,el)
7274 var col = Roo.get(el);
7276 if(!col.hasClass('sortable')){
7280 var sort = col.attr('sort');
7283 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7287 this.store.sortInfo = {field : sort, direction : dir};
7290 Roo.log("calling footer first");
7291 this.footer.onClick('first');
7294 this.store.load({ params : { start : 0 } });
7298 renderHeader : function()
7306 this.totalWidth = 0;
7308 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7310 var config = cm.config[i];
7314 cls : 'x-hcol-' + i,
7316 html: cm.getColumnHeader(i)
7321 if(typeof(config.sortable) != 'undefined' && config.sortable){
7323 c.html = '<i class="glyphicon"></i>' + c.html;
7326 // could use BS4 hidden-..-down
7328 if(typeof(config.lgHeader) != 'undefined'){
7329 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7332 if(typeof(config.mdHeader) != 'undefined'){
7333 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7336 if(typeof(config.smHeader) != 'undefined'){
7337 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7340 if(typeof(config.xsHeader) != 'undefined'){
7341 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7348 if(typeof(config.tooltip) != 'undefined'){
7349 c.tooltip = config.tooltip;
7352 if(typeof(config.colspan) != 'undefined'){
7353 c.colspan = config.colspan;
7356 if(typeof(config.hidden) != 'undefined' && config.hidden){
7357 c.style += ' display:none;';
7360 if(typeof(config.dataIndex) != 'undefined'){
7361 c.sort = config.dataIndex;
7366 if(typeof(config.align) != 'undefined' && config.align.length){
7367 c.style += ' text-align:' + config.align + ';';
7370 if(typeof(config.width) != 'undefined'){
7371 c.style += ' width:' + config.width + 'px;';
7372 this.totalWidth += config.width;
7374 this.totalWidth += 100; // assume minimum of 100 per column?
7377 if(typeof(config.cls) != 'undefined'){
7378 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7381 ['xs','sm','md','lg'].map(function(size){
7383 if(typeof(config[size]) == 'undefined'){
7387 if (!config[size]) { // 0 = hidden
7388 // BS 4 '0' is treated as hide that column and below.
7389 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7393 c.cls += ' col-' + size + '-' + config[size] + (
7394 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7406 renderBody : function()
7416 colspan : this.cm.getColumnCount()
7426 renderFooter : function()
7436 colspan : this.cm.getColumnCount()
7450 // Roo.log('ds onload');
7455 var ds = this.store;
7457 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7458 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7459 if (_this.store.sortInfo) {
7461 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7462 e.select('i', true).addClass(['glyphicon-arrow-up']);
7465 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7466 e.select('i', true).addClass(['glyphicon-arrow-down']);
7471 var tbody = this.mainBody;
7473 if(ds.getCount() > 0){
7474 ds.data.each(function(d,rowIndex){
7475 var row = this.renderRow(cm, ds, rowIndex);
7477 tbody.createChild(row);
7481 if(row.cellObjects.length){
7482 Roo.each(row.cellObjects, function(r){
7483 _this.renderCellObject(r);
7490 var tfoot = this.el.select('tfoot', true).first();
7492 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7494 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7496 var total = this.ds.getTotalCount();
7498 if(this.footer.pageSize < total){
7499 this.mainFoot.show();
7503 Roo.each(this.el.select('tbody td', true).elements, function(e){
7504 e.on('mouseover', _this.onMouseover, _this);
7507 Roo.each(this.el.select('tbody td', true).elements, function(e){
7508 e.on('mouseout', _this.onMouseout, _this);
7510 this.fireEvent('rowsrendered', this);
7516 onUpdate : function(ds,record)
7518 this.refreshRow(record);
7522 onRemove : function(ds, record, index, isUpdate){
7523 if(isUpdate !== true){
7524 this.fireEvent("beforerowremoved", this, index, record);
7526 var bt = this.mainBody.dom;
7528 var rows = this.el.select('tbody > tr', true).elements;
7530 if(typeof(rows[index]) != 'undefined'){
7531 bt.removeChild(rows[index].dom);
7534 // if(bt.rows[index]){
7535 // bt.removeChild(bt.rows[index]);
7538 if(isUpdate !== true){
7539 //this.stripeRows(index);
7540 //this.syncRowHeights(index, index);
7542 this.fireEvent("rowremoved", this, index, record);
7546 onAdd : function(ds, records, rowIndex)
7548 //Roo.log('on Add called');
7549 // - note this does not handle multiple adding very well..
7550 var bt = this.mainBody.dom;
7551 for (var i =0 ; i < records.length;i++) {
7552 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7553 //Roo.log(records[i]);
7554 //Roo.log(this.store.getAt(rowIndex+i));
7555 this.insertRow(this.store, rowIndex + i, false);
7562 refreshRow : function(record){
7563 var ds = this.store, index;
7564 if(typeof record == 'number'){
7566 record = ds.getAt(index);
7568 index = ds.indexOf(record);
7570 this.insertRow(ds, index, true);
7572 this.onRemove(ds, record, index+1, true);
7574 //this.syncRowHeights(index, index);
7576 this.fireEvent("rowupdated", this, index, record);
7579 insertRow : function(dm, rowIndex, isUpdate){
7582 this.fireEvent("beforerowsinserted", this, rowIndex);
7584 //var s = this.getScrollState();
7585 var row = this.renderRow(this.cm, this.store, rowIndex);
7586 // insert before rowIndex..
7587 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7591 if(row.cellObjects.length){
7592 Roo.each(row.cellObjects, function(r){
7593 _this.renderCellObject(r);
7598 this.fireEvent("rowsinserted", this, rowIndex);
7599 //this.syncRowHeights(firstRow, lastRow);
7600 //this.stripeRows(firstRow);
7607 getRowDom : function(rowIndex)
7609 var rows = this.el.select('tbody > tr', true).elements;
7611 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7614 // returns the object tree for a tr..
7617 renderRow : function(cm, ds, rowIndex)
7619 var d = ds.getAt(rowIndex);
7623 cls : 'x-row-' + rowIndex,
7627 var cellObjects = [];
7629 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7630 var config = cm.config[i];
7632 var renderer = cm.getRenderer(i);
7636 if(typeof(renderer) !== 'undefined'){
7637 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7639 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7640 // and are rendered into the cells after the row is rendered - using the id for the element.
7642 if(typeof(value) === 'object'){
7652 rowIndex : rowIndex,
7657 this.fireEvent('rowclass', this, rowcfg);
7661 cls : rowcfg.rowClass + ' x-col-' + i,
7663 html: (typeof(value) === 'object') ? '' : value
7670 if(typeof(config.colspan) != 'undefined'){
7671 td.colspan = config.colspan;
7674 if(typeof(config.hidden) != 'undefined' && config.hidden){
7675 td.style += ' display:none;';
7678 if(typeof(config.align) != 'undefined' && config.align.length){
7679 td.style += ' text-align:' + config.align + ';';
7681 if(typeof(config.valign) != 'undefined' && config.valign.length){
7682 td.style += ' vertical-align:' + config.valign + ';';
7685 if(typeof(config.width) != 'undefined'){
7686 td.style += ' width:' + config.width + 'px;';
7689 if(typeof(config.cursor) != 'undefined'){
7690 td.style += ' cursor:' + config.cursor + ';';
7693 if(typeof(config.cls) != 'undefined'){
7694 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7697 ['xs','sm','md','lg'].map(function(size){
7699 if(typeof(config[size]) == 'undefined'){
7705 if (!config[size]) { // 0 = hidden
7706 // BS 4 '0' is treated as hide that column and below.
7707 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7711 td.cls += ' col-' + size + '-' + config[size] + (
7712 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7722 row.cellObjects = cellObjects;
7730 onBeforeLoad : function()
7739 this.el.select('tbody', true).first().dom.innerHTML = '';
7742 * Show or hide a row.
7743 * @param {Number} rowIndex to show or hide
7744 * @param {Boolean} state hide
7746 setRowVisibility : function(rowIndex, state)
7748 var bt = this.mainBody.dom;
7750 var rows = this.el.select('tbody > tr', true).elements;
7752 if(typeof(rows[rowIndex]) == 'undefined'){
7755 rows[rowIndex].dom.style.display = state ? '' : 'none';
7759 getSelectionModel : function(){
7761 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7763 return this.selModel;
7766 * Render the Roo.bootstrap object from renderder
7768 renderCellObject : function(r)
7772 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7774 var t = r.cfg.render(r.container);
7777 Roo.each(r.cfg.cn, function(c){
7779 container: t.getChildContainer(),
7782 _this.renderCellObject(child);
7787 getRowIndex : function(row)
7791 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7802 * Returns the grid's underlying element = used by panel.Grid
7803 * @return {Element} The element
7805 getGridEl : function(){
7809 * Forces a resize - used by panel.Grid
7810 * @return {Element} The element
7812 autoSize : function()
7814 //var ctr = Roo.get(this.container.dom.parentElement);
7815 var ctr = Roo.get(this.el.dom);
7817 var thd = this.getGridEl().select('thead',true).first();
7818 var tbd = this.getGridEl().select('tbody', true).first();
7819 var tfd = this.getGridEl().select('tfoot', true).first();
7821 var cw = ctr.getWidth();
7825 tbd.setWidth(ctr.getWidth());
7826 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7827 // this needs fixing for various usage - currently only hydra job advers I think..
7829 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7831 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7834 cw = Math.max(cw, this.totalWidth);
7835 this.getGridEl().select('tr',true).setWidth(cw);
7836 // resize 'expandable coloumn?
7838 return; // we doe not have a view in this design..
7841 onBodyScroll: function()
7843 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7845 this.mainHead.setStyle({
7846 'position' : 'relative',
7847 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7853 var scrollHeight = this.mainBody.dom.scrollHeight;
7855 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7857 var height = this.mainBody.getHeight();
7859 if(scrollHeight - height == scrollTop) {
7861 var total = this.ds.getTotalCount();
7863 if(this.footer.cursor + this.footer.pageSize < total){
7865 this.footer.ds.load({
7867 start : this.footer.cursor + this.footer.pageSize,
7868 limit : this.footer.pageSize
7878 onHeaderChange : function()
7880 var header = this.renderHeader();
7881 var table = this.el.select('table', true).first();
7883 this.mainHead.remove();
7884 this.mainHead = table.createChild(header, this.mainBody, false);
7887 onHiddenChange : function(colModel, colIndex, hidden)
7889 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7890 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7892 this.CSS.updateRule(thSelector, "display", "");
7893 this.CSS.updateRule(tdSelector, "display", "");
7896 this.CSS.updateRule(thSelector, "display", "none");
7897 this.CSS.updateRule(tdSelector, "display", "none");
7900 this.onHeaderChange();
7904 setColumnWidth: function(col_index, width)
7906 // width = "md-2 xs-2..."
7907 if(!this.colModel.config[col_index]) {
7911 var w = width.split(" ");
7913 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7915 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7918 for(var j = 0; j < w.length; j++) {
7924 var size_cls = w[j].split("-");
7926 if(!Number.isInteger(size_cls[1] * 1)) {
7930 if(!this.colModel.config[col_index][size_cls[0]]) {
7934 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7938 h_row[0].classList.replace(
7939 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7940 "col-"+size_cls[0]+"-"+size_cls[1]
7943 for(var i = 0; i < rows.length; i++) {
7945 var size_cls = w[j].split("-");
7947 if(!Number.isInteger(size_cls[1] * 1)) {
7951 if(!this.colModel.config[col_index][size_cls[0]]) {
7955 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7959 rows[i].classList.replace(
7960 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7961 "col-"+size_cls[0]+"-"+size_cls[1]
7965 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7980 * @class Roo.bootstrap.TableCell
7981 * @extends Roo.bootstrap.Component
7982 * Bootstrap TableCell class
7983 * @cfg {String} html cell contain text
7984 * @cfg {String} cls cell class
7985 * @cfg {String} tag cell tag (td|th) default td
7986 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7987 * @cfg {String} align Aligns the content in a cell
7988 * @cfg {String} axis Categorizes cells
7989 * @cfg {String} bgcolor Specifies the background color of a cell
7990 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7991 * @cfg {Number} colspan Specifies the number of columns a cell should span
7992 * @cfg {String} headers Specifies one or more header cells a cell is related to
7993 * @cfg {Number} height Sets the height of a cell
7994 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7995 * @cfg {Number} rowspan Sets the number of rows a cell should span
7996 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7997 * @cfg {String} valign Vertical aligns the content in a cell
7998 * @cfg {Number} width Specifies the width of a cell
8001 * Create a new TableCell
8002 * @param {Object} config The config object
8005 Roo.bootstrap.TableCell = function(config){
8006 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8009 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8029 getAutoCreate : function(){
8030 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8050 cfg.align=this.align
8056 cfg.bgcolor=this.bgcolor
8059 cfg.charoff=this.charoff
8062 cfg.colspan=this.colspan
8065 cfg.headers=this.headers
8068 cfg.height=this.height
8071 cfg.nowrap=this.nowrap
8074 cfg.rowspan=this.rowspan
8077 cfg.scope=this.scope
8080 cfg.valign=this.valign
8083 cfg.width=this.width
8102 * @class Roo.bootstrap.TableRow
8103 * @extends Roo.bootstrap.Component
8104 * Bootstrap TableRow class
8105 * @cfg {String} cls row class
8106 * @cfg {String} align Aligns the content in a table row
8107 * @cfg {String} bgcolor Specifies a background color for a table row
8108 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8109 * @cfg {String} valign Vertical aligns the content in a table row
8112 * Create a new TableRow
8113 * @param {Object} config The config object
8116 Roo.bootstrap.TableRow = function(config){
8117 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8120 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8128 getAutoCreate : function(){
8129 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8139 cfg.align = this.align;
8142 cfg.bgcolor = this.bgcolor;
8145 cfg.charoff = this.charoff;
8148 cfg.valign = this.valign;
8166 * @class Roo.bootstrap.TableBody
8167 * @extends Roo.bootstrap.Component
8168 * Bootstrap TableBody class
8169 * @cfg {String} cls element class
8170 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8171 * @cfg {String} align Aligns the content inside the element
8172 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8173 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8176 * Create a new TableBody
8177 * @param {Object} config The config object
8180 Roo.bootstrap.TableBody = function(config){
8181 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8184 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8192 getAutoCreate : function(){
8193 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8207 cfg.align = this.align;
8210 cfg.charoff = this.charoff;
8213 cfg.valign = this.valign;
8220 // initEvents : function()
8227 // this.store = Roo.factory(this.store, Roo.data);
8228 // this.store.on('load', this.onLoad, this);
8230 // this.store.load();
8234 // onLoad: function ()
8236 // this.fireEvent('load', this);
8246 * Ext JS Library 1.1.1
8247 * Copyright(c) 2006-2007, Ext JS, LLC.
8249 * Originally Released Under LGPL - original licence link has changed is not relivant.
8252 * <script type="text/javascript">
8255 // as we use this in bootstrap.
8256 Roo.namespace('Roo.form');
8258 * @class Roo.form.Action
8259 * Internal Class used to handle form actions
8261 * @param {Roo.form.BasicForm} el The form element or its id
8262 * @param {Object} config Configuration options
8267 // define the action interface
8268 Roo.form.Action = function(form, options){
8270 this.options = options || {};
8273 * Client Validation Failed
8276 Roo.form.Action.CLIENT_INVALID = 'client';
8278 * Server Validation Failed
8281 Roo.form.Action.SERVER_INVALID = 'server';
8283 * Connect to Server Failed
8286 Roo.form.Action.CONNECT_FAILURE = 'connect';
8288 * Reading Data from Server Failed
8291 Roo.form.Action.LOAD_FAILURE = 'load';
8293 Roo.form.Action.prototype = {
8295 failureType : undefined,
8296 response : undefined,
8300 run : function(options){
8305 success : function(response){
8310 handleResponse : function(response){
8314 // default connection failure
8315 failure : function(response){
8317 this.response = response;
8318 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8319 this.form.afterAction(this, false);
8322 processResponse : function(response){
8323 this.response = response;
8324 if(!response.responseText){
8327 this.result = this.handleResponse(response);
8331 // utility functions used internally
8332 getUrl : function(appendParams){
8333 var url = this.options.url || this.form.url || this.form.el.dom.action;
8335 var p = this.getParams();
8337 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8343 getMethod : function(){
8344 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8347 getParams : function(){
8348 var bp = this.form.baseParams;
8349 var p = this.options.params;
8351 if(typeof p == "object"){
8352 p = Roo.urlEncode(Roo.applyIf(p, bp));
8353 }else if(typeof p == 'string' && bp){
8354 p += '&' + Roo.urlEncode(bp);
8357 p = Roo.urlEncode(bp);
8362 createCallback : function(){
8364 success: this.success,
8365 failure: this.failure,
8367 timeout: (this.form.timeout*1000),
8368 upload: this.form.fileUpload ? this.success : undefined
8373 Roo.form.Action.Submit = function(form, options){
8374 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8377 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8380 haveProgress : false,
8381 uploadComplete : false,
8383 // uploadProgress indicator.
8384 uploadProgress : function()
8386 if (!this.form.progressUrl) {
8390 if (!this.haveProgress) {
8391 Roo.MessageBox.progress("Uploading", "Uploading");
8393 if (this.uploadComplete) {
8394 Roo.MessageBox.hide();
8398 this.haveProgress = true;
8400 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8402 var c = new Roo.data.Connection();
8404 url : this.form.progressUrl,
8409 success : function(req){
8410 //console.log(data);
8414 rdata = Roo.decode(req.responseText)
8416 Roo.log("Invalid data from server..");
8420 if (!rdata || !rdata.success) {
8422 Roo.MessageBox.alert(Roo.encode(rdata));
8425 var data = rdata.data;
8427 if (this.uploadComplete) {
8428 Roo.MessageBox.hide();
8433 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8434 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8437 this.uploadProgress.defer(2000,this);
8440 failure: function(data) {
8441 Roo.log('progress url failed ');
8452 // run get Values on the form, so it syncs any secondary forms.
8453 this.form.getValues();
8455 var o = this.options;
8456 var method = this.getMethod();
8457 var isPost = method == 'POST';
8458 if(o.clientValidation === false || this.form.isValid()){
8460 if (this.form.progressUrl) {
8461 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8462 (new Date() * 1) + '' + Math.random());
8467 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8468 form:this.form.el.dom,
8469 url:this.getUrl(!isPost),
8471 params:isPost ? this.getParams() : null,
8472 isUpload: this.form.fileUpload,
8473 formData : this.form.formData
8476 this.uploadProgress();
8478 }else if (o.clientValidation !== false){ // client validation failed
8479 this.failureType = Roo.form.Action.CLIENT_INVALID;
8480 this.form.afterAction(this, false);
8484 success : function(response)
8486 this.uploadComplete= true;
8487 if (this.haveProgress) {
8488 Roo.MessageBox.hide();
8492 var result = this.processResponse(response);
8493 if(result === true || result.success){
8494 this.form.afterAction(this, true);
8498 this.form.markInvalid(result.errors);
8499 this.failureType = Roo.form.Action.SERVER_INVALID;
8501 this.form.afterAction(this, false);
8503 failure : function(response)
8505 this.uploadComplete= true;
8506 if (this.haveProgress) {
8507 Roo.MessageBox.hide();
8510 this.response = response;
8511 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8512 this.form.afterAction(this, false);
8515 handleResponse : function(response){
8516 if(this.form.errorReader){
8517 var rs = this.form.errorReader.read(response);
8520 for(var i = 0, len = rs.records.length; i < len; i++) {
8521 var r = rs.records[i];
8525 if(errors.length < 1){
8529 success : rs.success,
8535 ret = Roo.decode(response.responseText);
8539 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8549 Roo.form.Action.Load = function(form, options){
8550 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8551 this.reader = this.form.reader;
8554 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8559 Roo.Ajax.request(Roo.apply(
8560 this.createCallback(), {
8561 method:this.getMethod(),
8562 url:this.getUrl(false),
8563 params:this.getParams()
8567 success : function(response){
8569 var result = this.processResponse(response);
8570 if(result === true || !result.success || !result.data){
8571 this.failureType = Roo.form.Action.LOAD_FAILURE;
8572 this.form.afterAction(this, false);
8575 this.form.clearInvalid();
8576 this.form.setValues(result.data);
8577 this.form.afterAction(this, true);
8580 handleResponse : function(response){
8581 if(this.form.reader){
8582 var rs = this.form.reader.read(response);
8583 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8585 success : rs.success,
8589 return Roo.decode(response.responseText);
8593 Roo.form.Action.ACTION_TYPES = {
8594 'load' : Roo.form.Action.Load,
8595 'submit' : Roo.form.Action.Submit
8604 * @class Roo.bootstrap.Form
8605 * @extends Roo.bootstrap.Component
8606 * Bootstrap Form class
8607 * @cfg {String} method GET | POST (default POST)
8608 * @cfg {String} labelAlign top | left (default top)
8609 * @cfg {String} align left | right - for navbars
8610 * @cfg {Boolean} loadMask load mask when submit (default true)
8615 * @param {Object} config The config object
8619 Roo.bootstrap.Form = function(config){
8621 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8623 Roo.bootstrap.Form.popover.apply();
8627 * @event clientvalidation
8628 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8629 * @param {Form} this
8630 * @param {Boolean} valid true if the form has passed client-side validation
8632 clientvalidation: true,
8634 * @event beforeaction
8635 * Fires before any action is performed. Return false to cancel the action.
8636 * @param {Form} this
8637 * @param {Action} action The action to be performed
8641 * @event actionfailed
8642 * Fires when an action fails.
8643 * @param {Form} this
8644 * @param {Action} action The action that failed
8646 actionfailed : true,
8648 * @event actioncomplete
8649 * Fires when an action is completed.
8650 * @param {Form} this
8651 * @param {Action} action The action that completed
8653 actioncomplete : true
8657 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8660 * @cfg {String} method
8661 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8666 * The URL to use for form actions if one isn't supplied in the action options.
8669 * @cfg {Boolean} fileUpload
8670 * Set to true if this form is a file upload.
8674 * @cfg {Object} baseParams
8675 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8679 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8683 * @cfg {Sting} align (left|right) for navbar forms
8688 activeAction : null,
8691 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8692 * element by passing it or its id or mask the form itself by passing in true.
8695 waitMsgTarget : false,
8700 * @cfg {Boolean} errorMask (true|false) default false
8705 * @cfg {Number} maskOffset Default 100
8710 * @cfg {Boolean} maskBody
8714 getAutoCreate : function(){
8718 method : this.method || 'POST',
8719 id : this.id || Roo.id(),
8722 if (this.parent().xtype.match(/^Nav/)) {
8723 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8727 if (this.labelAlign == 'left' ) {
8728 cfg.cls += ' form-horizontal';
8734 initEvents : function()
8736 this.el.on('submit', this.onSubmit, this);
8737 // this was added as random key presses on the form where triggering form submit.
8738 this.el.on('keypress', function(e) {
8739 if (e.getCharCode() != 13) {
8742 // we might need to allow it for textareas.. and some other items.
8743 // check e.getTarget().
8745 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8749 Roo.log("keypress blocked");
8757 onSubmit : function(e){
8762 * Returns true if client-side validation on the form is successful.
8765 isValid : function(){
8766 var items = this.getItems();
8770 items.each(function(f){
8776 Roo.log('invalid field: ' + f.name);
8780 if(!target && f.el.isVisible(true)){
8786 if(this.errorMask && !valid){
8787 Roo.bootstrap.Form.popover.mask(this, target);
8794 * Returns true if any fields in this form have changed since their original load.
8797 isDirty : function(){
8799 var items = this.getItems();
8800 items.each(function(f){
8810 * Performs a predefined action (submit or load) or custom actions you define on this form.
8811 * @param {String} actionName The name of the action type
8812 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8813 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8814 * accept other config options):
8816 Property Type Description
8817 ---------------- --------------- ----------------------------------------------------------------------------------
8818 url String The url for the action (defaults to the form's url)
8819 method String The form method to use (defaults to the form's method, or POST if not defined)
8820 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8821 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8822 validate the form on the client (defaults to false)
8824 * @return {BasicForm} this
8826 doAction : function(action, options){
8827 if(typeof action == 'string'){
8828 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8830 if(this.fireEvent('beforeaction', this, action) !== false){
8831 this.beforeAction(action);
8832 action.run.defer(100, action);
8838 beforeAction : function(action){
8839 var o = action.options;
8844 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8846 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8849 // not really supported yet.. ??
8851 //if(this.waitMsgTarget === true){
8852 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8853 //}else if(this.waitMsgTarget){
8854 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8855 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8857 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8863 afterAction : function(action, success){
8864 this.activeAction = null;
8865 var o = action.options;
8870 Roo.get(document.body).unmask();
8876 //if(this.waitMsgTarget === true){
8877 // this.el.unmask();
8878 //}else if(this.waitMsgTarget){
8879 // this.waitMsgTarget.unmask();
8881 // Roo.MessageBox.updateProgress(1);
8882 // Roo.MessageBox.hide();
8889 Roo.callback(o.success, o.scope, [this, action]);
8890 this.fireEvent('actioncomplete', this, action);
8894 // failure condition..
8895 // we have a scenario where updates need confirming.
8896 // eg. if a locking scenario exists..
8897 // we look for { errors : { needs_confirm : true }} in the response.
8899 (typeof(action.result) != 'undefined') &&
8900 (typeof(action.result.errors) != 'undefined') &&
8901 (typeof(action.result.errors.needs_confirm) != 'undefined')
8904 Roo.log("not supported yet");
8907 Roo.MessageBox.confirm(
8908 "Change requires confirmation",
8909 action.result.errorMsg,
8914 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8924 Roo.callback(o.failure, o.scope, [this, action]);
8925 // show an error message if no failed handler is set..
8926 if (!this.hasListener('actionfailed')) {
8927 Roo.log("need to add dialog support");
8929 Roo.MessageBox.alert("Error",
8930 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8931 action.result.errorMsg :
8932 "Saving Failed, please check your entries or try again"
8937 this.fireEvent('actionfailed', this, action);
8942 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8943 * @param {String} id The value to search for
8946 findField : function(id){
8947 var items = this.getItems();
8948 var field = items.get(id);
8950 items.each(function(f){
8951 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8958 return field || null;
8961 * Mark fields in this form invalid in bulk.
8962 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8963 * @return {BasicForm} this
8965 markInvalid : function(errors){
8966 if(errors instanceof Array){
8967 for(var i = 0, len = errors.length; i < len; i++){
8968 var fieldError = errors[i];
8969 var f = this.findField(fieldError.id);
8971 f.markInvalid(fieldError.msg);
8977 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8978 field.markInvalid(errors[id]);
8982 //Roo.each(this.childForms || [], function (f) {
8983 // f.markInvalid(errors);
8990 * Set values for fields in this form in bulk.
8991 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8992 * @return {BasicForm} this
8994 setValues : function(values){
8995 if(values instanceof Array){ // array of objects
8996 for(var i = 0, len = values.length; i < len; i++){
8998 var f = this.findField(v.id);
9000 f.setValue(v.value);
9001 if(this.trackResetOnLoad){
9002 f.originalValue = f.getValue();
9006 }else{ // object hash
9009 if(typeof values[id] != 'function' && (field = this.findField(id))){
9011 if (field.setFromData &&
9013 field.displayField &&
9014 // combos' with local stores can
9015 // be queried via setValue()
9016 // to set their value..
9017 (field.store && !field.store.isLocal)
9021 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9022 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9023 field.setFromData(sd);
9025 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9027 field.setFromData(values);
9030 field.setValue(values[id]);
9034 if(this.trackResetOnLoad){
9035 field.originalValue = field.getValue();
9041 //Roo.each(this.childForms || [], function (f) {
9042 // f.setValues(values);
9049 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9050 * they are returned as an array.
9051 * @param {Boolean} asString
9054 getValues : function(asString){
9055 //if (this.childForms) {
9056 // copy values from the child forms
9057 // Roo.each(this.childForms, function (f) {
9058 // this.setValues(f.getValues());
9064 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9065 if(asString === true){
9068 return Roo.urlDecode(fs);
9072 * Returns the fields in this form as an object with key/value pairs.
9073 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9076 getFieldValues : function(with_hidden)
9078 var items = this.getItems();
9080 items.each(function(f){
9086 var v = f.getValue();
9088 if (f.inputType =='radio') {
9089 if (typeof(ret[f.getName()]) == 'undefined') {
9090 ret[f.getName()] = ''; // empty..
9093 if (!f.el.dom.checked) {
9101 if(f.xtype == 'MoneyField'){
9102 ret[f.currencyName] = f.getCurrency();
9105 // not sure if this supported any more..
9106 if ((typeof(v) == 'object') && f.getRawValue) {
9107 v = f.getRawValue() ; // dates..
9109 // combo boxes where name != hiddenName...
9110 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9111 ret[f.name] = f.getRawValue();
9113 ret[f.getName()] = v;
9120 * Clears all invalid messages in this form.
9121 * @return {BasicForm} this
9123 clearInvalid : function(){
9124 var items = this.getItems();
9126 items.each(function(f){
9135 * @return {BasicForm} this
9138 var items = this.getItems();
9139 items.each(function(f){
9143 Roo.each(this.childForms || [], function (f) {
9151 getItems : function()
9153 var r=new Roo.util.MixedCollection(false, function(o){
9154 return o.id || (o.id = Roo.id());
9156 var iter = function(el) {
9163 Roo.each(el.items,function(e) {
9172 hideFields : function(items)
9174 Roo.each(items, function(i){
9176 var f = this.findField(i);
9187 showFields : function(items)
9189 Roo.each(items, function(i){
9191 var f = this.findField(i);
9204 Roo.apply(Roo.bootstrap.Form, {
9231 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9232 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9233 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9234 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9237 this.maskEl.top.enableDisplayMode("block");
9238 this.maskEl.left.enableDisplayMode("block");
9239 this.maskEl.bottom.enableDisplayMode("block");
9240 this.maskEl.right.enableDisplayMode("block");
9242 this.toolTip = new Roo.bootstrap.Tooltip({
9243 cls : 'roo-form-error-popover',
9245 'left' : ['r-l', [-2,0], 'right'],
9246 'right' : ['l-r', [2,0], 'left'],
9247 'bottom' : ['tl-bl', [0,2], 'top'],
9248 'top' : [ 'bl-tl', [0,-2], 'bottom']
9252 this.toolTip.render(Roo.get(document.body));
9254 this.toolTip.el.enableDisplayMode("block");
9256 Roo.get(document.body).on('click', function(){
9260 Roo.get(document.body).on('touchstart', function(){
9264 this.isApplied = true
9267 mask : function(form, target)
9271 this.target = target;
9273 if(!this.form.errorMask || !target.el){
9277 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9279 Roo.log(scrollable);
9281 var ot = this.target.el.calcOffsetsTo(scrollable);
9283 var scrollTo = ot[1] - this.form.maskOffset;
9285 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9287 scrollable.scrollTo('top', scrollTo);
9289 var box = this.target.el.getBox();
9291 var zIndex = Roo.bootstrap.Modal.zIndex++;
9294 this.maskEl.top.setStyle('position', 'absolute');
9295 this.maskEl.top.setStyle('z-index', zIndex);
9296 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9297 this.maskEl.top.setLeft(0);
9298 this.maskEl.top.setTop(0);
9299 this.maskEl.top.show();
9301 this.maskEl.left.setStyle('position', 'absolute');
9302 this.maskEl.left.setStyle('z-index', zIndex);
9303 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9304 this.maskEl.left.setLeft(0);
9305 this.maskEl.left.setTop(box.y - this.padding);
9306 this.maskEl.left.show();
9308 this.maskEl.bottom.setStyle('position', 'absolute');
9309 this.maskEl.bottom.setStyle('z-index', zIndex);
9310 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9311 this.maskEl.bottom.setLeft(0);
9312 this.maskEl.bottom.setTop(box.bottom + this.padding);
9313 this.maskEl.bottom.show();
9315 this.maskEl.right.setStyle('position', 'absolute');
9316 this.maskEl.right.setStyle('z-index', zIndex);
9317 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9318 this.maskEl.right.setLeft(box.right + this.padding);
9319 this.maskEl.right.setTop(box.y - this.padding);
9320 this.maskEl.right.show();
9322 this.toolTip.bindEl = this.target.el;
9324 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9326 var tip = this.target.blankText;
9328 if(this.target.getValue() !== '' ) {
9330 if (this.target.invalidText.length) {
9331 tip = this.target.invalidText;
9332 } else if (this.target.regexText.length){
9333 tip = this.target.regexText;
9337 this.toolTip.show(tip);
9339 this.intervalID = window.setInterval(function() {
9340 Roo.bootstrap.Form.popover.unmask();
9343 window.onwheel = function(){ return false;};
9345 (function(){ this.isMasked = true; }).defer(500, this);
9351 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9355 this.maskEl.top.setStyle('position', 'absolute');
9356 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9357 this.maskEl.top.hide();
9359 this.maskEl.left.setStyle('position', 'absolute');
9360 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9361 this.maskEl.left.hide();
9363 this.maskEl.bottom.setStyle('position', 'absolute');
9364 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9365 this.maskEl.bottom.hide();
9367 this.maskEl.right.setStyle('position', 'absolute');
9368 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9369 this.maskEl.right.hide();
9371 this.toolTip.hide();
9373 this.toolTip.el.hide();
9375 window.onwheel = function(){ return true;};
9377 if(this.intervalID){
9378 window.clearInterval(this.intervalID);
9379 this.intervalID = false;
9382 this.isMasked = false;
9392 * Ext JS Library 1.1.1
9393 * Copyright(c) 2006-2007, Ext JS, LLC.
9395 * Originally Released Under LGPL - original licence link has changed is not relivant.
9398 * <script type="text/javascript">
9401 * @class Roo.form.VTypes
9402 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9405 Roo.form.VTypes = function(){
9406 // closure these in so they are only created once.
9407 var alpha = /^[a-zA-Z_]+$/;
9408 var alphanum = /^[a-zA-Z0-9_]+$/;
9409 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9410 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9412 // All these messages and functions are configurable
9415 * The function used to validate email addresses
9416 * @param {String} value The email address
9418 'email' : function(v){
9419 return email.test(v);
9422 * The error text to display when the email validation function returns false
9425 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9427 * The keystroke filter mask to be applied on email input
9430 'emailMask' : /[a-z0-9_\.\-@]/i,
9433 * The function used to validate URLs
9434 * @param {String} value The URL
9436 'url' : function(v){
9440 * The error text to display when the url validation function returns false
9443 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9446 * The function used to validate alpha values
9447 * @param {String} value The value
9449 'alpha' : function(v){
9450 return alpha.test(v);
9453 * The error text to display when the alpha validation function returns false
9456 'alphaText' : 'This field should only contain letters and _',
9458 * The keystroke filter mask to be applied on alpha input
9461 'alphaMask' : /[a-z_]/i,
9464 * The function used to validate alphanumeric values
9465 * @param {String} value The value
9467 'alphanum' : function(v){
9468 return alphanum.test(v);
9471 * The error text to display when the alphanumeric validation function returns false
9474 'alphanumText' : 'This field should only contain letters, numbers and _',
9476 * The keystroke filter mask to be applied on alphanumeric input
9479 'alphanumMask' : /[a-z0-9_]/i
9489 * @class Roo.bootstrap.Input
9490 * @extends Roo.bootstrap.Component
9491 * Bootstrap Input class
9492 * @cfg {Boolean} disabled is it disabled
9493 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9494 * @cfg {String} name name of the input
9495 * @cfg {string} fieldLabel - the label associated
9496 * @cfg {string} placeholder - placeholder to put in text.
9497 * @cfg {string} before - input group add on before
9498 * @cfg {string} after - input group add on after
9499 * @cfg {string} size - (lg|sm) or leave empty..
9500 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9501 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9502 * @cfg {Number} md colspan out of 12 for computer-sized screens
9503 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9504 * @cfg {string} value default value of the input
9505 * @cfg {Number} labelWidth set the width of label
9506 * @cfg {Number} labellg set the width of label (1-12)
9507 * @cfg {Number} labelmd set the width of label (1-12)
9508 * @cfg {Number} labelsm set the width of label (1-12)
9509 * @cfg {Number} labelxs set the width of label (1-12)
9510 * @cfg {String} labelAlign (top|left)
9511 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9512 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9513 * @cfg {String} indicatorpos (left|right) default left
9514 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9515 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9517 * @cfg {String} align (left|center|right) Default left
9518 * @cfg {Boolean} forceFeedback (true|false) Default false
9521 * Create a new Input
9522 * @param {Object} config The config object
9525 Roo.bootstrap.Input = function(config){
9527 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9532 * Fires when this field receives input focus.
9533 * @param {Roo.form.Field} this
9538 * Fires when this field loses input focus.
9539 * @param {Roo.form.Field} this
9544 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9545 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9546 * @param {Roo.form.Field} this
9547 * @param {Roo.EventObject} e The event object
9552 * Fires just before the field blurs if the field value has changed.
9553 * @param {Roo.form.Field} this
9554 * @param {Mixed} newValue The new value
9555 * @param {Mixed} oldValue The original value
9560 * Fires after the field has been marked as invalid.
9561 * @param {Roo.form.Field} this
9562 * @param {String} msg The validation message
9567 * Fires after the field has been validated with no errors.
9568 * @param {Roo.form.Field} this
9573 * Fires after the key up
9574 * @param {Roo.form.Field} this
9575 * @param {Roo.EventObject} e The event Object
9581 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9583 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9584 automatic validation (defaults to "keyup").
9586 validationEvent : "keyup",
9588 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9590 validateOnBlur : true,
9592 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9594 validationDelay : 250,
9596 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9598 focusClass : "x-form-focus", // not needed???
9602 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9604 invalidClass : "has-warning",
9607 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9609 validClass : "has-success",
9612 * @cfg {Boolean} hasFeedback (true|false) default true
9617 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9619 invalidFeedbackClass : "glyphicon-warning-sign",
9622 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9624 validFeedbackClass : "glyphicon-ok",
9627 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9629 selectOnFocus : false,
9632 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9636 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9641 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9643 disableKeyFilter : false,
9646 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9650 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9654 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9656 blankText : "Please complete this mandatory field",
9659 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9663 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9665 maxLength : Number.MAX_VALUE,
9667 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9669 minLengthText : "The minimum length for this field is {0}",
9671 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9673 maxLengthText : "The maximum length for this field is {0}",
9677 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9678 * If available, this function will be called only after the basic validators all return true, and will be passed the
9679 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9683 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9684 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9685 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9689 * @cfg {String} regexText -- Depricated - use Invalid Text
9694 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9700 autocomplete: false,
9719 formatedValue : false,
9720 forceFeedback : false,
9722 indicatorpos : 'left',
9732 parentLabelAlign : function()
9735 while (parent.parent()) {
9736 parent = parent.parent();
9737 if (typeof(parent.labelAlign) !='undefined') {
9738 return parent.labelAlign;
9745 getAutoCreate : function()
9747 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9753 if(this.inputType != 'hidden'){
9754 cfg.cls = 'form-group' //input-group
9760 type : this.inputType,
9762 cls : 'form-control',
9763 placeholder : this.placeholder || '',
9764 autocomplete : this.autocomplete || 'new-password'
9767 if(this.capture.length){
9768 input.capture = this.capture;
9771 if(this.accept.length){
9772 input.accept = this.accept + "/*";
9776 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9779 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9780 input.maxLength = this.maxLength;
9783 if (this.disabled) {
9784 input.disabled=true;
9787 if (this.readOnly) {
9788 input.readonly=true;
9792 input.name = this.name;
9796 input.cls += ' input-' + this.size;
9800 ['xs','sm','md','lg'].map(function(size){
9801 if (settings[size]) {
9802 cfg.cls += ' col-' + size + '-' + settings[size];
9806 var inputblock = input;
9810 cls: 'glyphicon form-control-feedback'
9813 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9816 cls : 'has-feedback',
9824 if (this.before || this.after) {
9827 cls : 'input-group',
9831 if (this.before && typeof(this.before) == 'string') {
9833 inputblock.cn.push({
9835 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9839 if (this.before && typeof(this.before) == 'object') {
9840 this.before = Roo.factory(this.before);
9842 inputblock.cn.push({
9844 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9845 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9849 inputblock.cn.push(input);
9851 if (this.after && typeof(this.after) == 'string') {
9852 inputblock.cn.push({
9854 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9858 if (this.after && typeof(this.after) == 'object') {
9859 this.after = Roo.factory(this.after);
9861 inputblock.cn.push({
9863 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9864 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9868 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9869 inputblock.cls += ' has-feedback';
9870 inputblock.cn.push(feedback);
9875 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9876 tooltip : 'This field is required'
9878 if (Roo.bootstrap.version == 4) {
9881 style : 'display-none'
9884 if (align ==='left' && this.fieldLabel.length) {
9886 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9893 cls : 'control-label col-form-label',
9894 html : this.fieldLabel
9905 var labelCfg = cfg.cn[1];
9906 var contentCfg = cfg.cn[2];
9908 if(this.indicatorpos == 'right'){
9913 cls : 'control-label col-form-label',
9917 html : this.fieldLabel
9931 labelCfg = cfg.cn[0];
9932 contentCfg = cfg.cn[1];
9936 if(this.labelWidth > 12){
9937 labelCfg.style = "width: " + this.labelWidth + 'px';
9940 if(this.labelWidth < 13 && this.labelmd == 0){
9941 this.labelmd = this.labelWidth;
9944 if(this.labellg > 0){
9945 labelCfg.cls += ' col-lg-' + this.labellg;
9946 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9949 if(this.labelmd > 0){
9950 labelCfg.cls += ' col-md-' + this.labelmd;
9951 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9954 if(this.labelsm > 0){
9955 labelCfg.cls += ' col-sm-' + this.labelsm;
9956 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9959 if(this.labelxs > 0){
9960 labelCfg.cls += ' col-xs-' + this.labelxs;
9961 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9965 } else if ( this.fieldLabel.length) {
9970 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9971 tooltip : 'This field is required'
9975 //cls : 'input-group-addon',
9976 html : this.fieldLabel
9984 if(this.indicatorpos == 'right'){
9989 //cls : 'input-group-addon',
9990 html : this.fieldLabel
9995 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9996 tooltip : 'This field is required'
10016 if (this.parentType === 'Navbar' && this.parent().bar) {
10017 cfg.cls += ' navbar-form';
10020 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10021 // on BS4 we do this only if not form
10022 cfg.cls += ' navbar-form';
10030 * return the real input element.
10032 inputEl: function ()
10034 return this.el.select('input.form-control',true).first();
10037 tooltipEl : function()
10039 return this.inputEl();
10042 indicatorEl : function()
10044 if (Roo.bootstrap.version == 4) {
10045 return false; // not enabled in v4 yet.
10048 var indicator = this.el.select('i.roo-required-indicator',true).first();
10058 setDisabled : function(v)
10060 var i = this.inputEl().dom;
10062 i.removeAttribute('disabled');
10066 i.setAttribute('disabled','true');
10068 initEvents : function()
10071 this.inputEl().on("keydown" , this.fireKey, this);
10072 this.inputEl().on("focus", this.onFocus, this);
10073 this.inputEl().on("blur", this.onBlur, this);
10075 this.inputEl().relayEvent('keyup', this);
10077 this.indicator = this.indicatorEl();
10079 if(this.indicator){
10080 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10083 // reference to original value for reset
10084 this.originalValue = this.getValue();
10085 //Roo.form.TextField.superclass.initEvents.call(this);
10086 if(this.validationEvent == 'keyup'){
10087 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10088 this.inputEl().on('keyup', this.filterValidation, this);
10090 else if(this.validationEvent !== false){
10091 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10094 if(this.selectOnFocus){
10095 this.on("focus", this.preFocus, this);
10098 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10099 this.inputEl().on("keypress", this.filterKeys, this);
10101 this.inputEl().relayEvent('keypress', this);
10104 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10105 this.el.on("click", this.autoSize, this);
10108 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10109 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10112 if (typeof(this.before) == 'object') {
10113 this.before.render(this.el.select('.roo-input-before',true).first());
10115 if (typeof(this.after) == 'object') {
10116 this.after.render(this.el.select('.roo-input-after',true).first());
10119 this.inputEl().on('change', this.onChange, this);
10122 filterValidation : function(e){
10123 if(!e.isNavKeyPress()){
10124 this.validationTask.delay(this.validationDelay);
10128 * Validates the field value
10129 * @return {Boolean} True if the value is valid, else false
10131 validate : function(){
10132 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10133 if(this.disabled || this.validateValue(this.getRawValue())){
10138 this.markInvalid();
10144 * Validates a value according to the field's validation rules and marks the field as invalid
10145 * if the validation fails
10146 * @param {Mixed} value The value to validate
10147 * @return {Boolean} True if the value is valid, else false
10149 validateValue : function(value)
10151 if(this.getVisibilityEl().hasClass('hidden')){
10155 if(value.length < 1) { // if it's blank
10156 if(this.allowBlank){
10162 if(value.length < this.minLength){
10165 if(value.length > this.maxLength){
10169 var vt = Roo.form.VTypes;
10170 if(!vt[this.vtype](value, this)){
10174 if(typeof this.validator == "function"){
10175 var msg = this.validator(value);
10179 if (typeof(msg) == 'string') {
10180 this.invalidText = msg;
10184 if(this.regex && !this.regex.test(value)){
10192 fireKey : function(e){
10193 //Roo.log('field ' + e.getKey());
10194 if(e.isNavKeyPress()){
10195 this.fireEvent("specialkey", this, e);
10198 focus : function (selectText){
10200 this.inputEl().focus();
10201 if(selectText === true){
10202 this.inputEl().dom.select();
10208 onFocus : function(){
10209 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10210 // this.el.addClass(this.focusClass);
10212 if(!this.hasFocus){
10213 this.hasFocus = true;
10214 this.startValue = this.getValue();
10215 this.fireEvent("focus", this);
10219 beforeBlur : Roo.emptyFn,
10223 onBlur : function(){
10225 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10226 //this.el.removeClass(this.focusClass);
10228 this.hasFocus = false;
10229 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10232 var v = this.getValue();
10233 if(String(v) !== String(this.startValue)){
10234 this.fireEvent('change', this, v, this.startValue);
10236 this.fireEvent("blur", this);
10239 onChange : function(e)
10241 var v = this.getValue();
10242 if(String(v) !== String(this.startValue)){
10243 this.fireEvent('change', this, v, this.startValue);
10249 * Resets the current field value to the originally loaded value and clears any validation messages
10251 reset : function(){
10252 this.setValue(this.originalValue);
10256 * Returns the name of the field
10257 * @return {Mixed} name The name field
10259 getName: function(){
10263 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10264 * @return {Mixed} value The field value
10266 getValue : function(){
10268 var v = this.inputEl().getValue();
10273 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10274 * @return {Mixed} value The field value
10276 getRawValue : function(){
10277 var v = this.inputEl().getValue();
10283 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10284 * @param {Mixed} value The value to set
10286 setRawValue : function(v){
10287 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10290 selectText : function(start, end){
10291 var v = this.getRawValue();
10293 start = start === undefined ? 0 : start;
10294 end = end === undefined ? v.length : end;
10295 var d = this.inputEl().dom;
10296 if(d.setSelectionRange){
10297 d.setSelectionRange(start, end);
10298 }else if(d.createTextRange){
10299 var range = d.createTextRange();
10300 range.moveStart("character", start);
10301 range.moveEnd("character", v.length-end);
10308 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10309 * @param {Mixed} value The value to set
10311 setValue : function(v){
10314 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10320 processValue : function(value){
10321 if(this.stripCharsRe){
10322 var newValue = value.replace(this.stripCharsRe, '');
10323 if(newValue !== value){
10324 this.setRawValue(newValue);
10331 preFocus : function(){
10333 if(this.selectOnFocus){
10334 this.inputEl().dom.select();
10337 filterKeys : function(e){
10338 var k = e.getKey();
10339 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10342 var c = e.getCharCode(), cc = String.fromCharCode(c);
10343 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10346 if(!this.maskRe.test(cc)){
10351 * Clear any invalid styles/messages for this field
10353 clearInvalid : function(){
10355 if(!this.el || this.preventMark){ // not rendered
10360 this.el.removeClass([this.invalidClass, 'is-invalid']);
10362 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10364 var feedback = this.el.select('.form-control-feedback', true).first();
10367 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10372 if(this.indicator){
10373 this.indicator.removeClass('visible');
10374 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10377 this.fireEvent('valid', this);
10381 * Mark this field as valid
10383 markValid : function()
10385 if(!this.el || this.preventMark){ // not rendered...
10389 this.el.removeClass([this.invalidClass, this.validClass]);
10390 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10392 var feedback = this.el.select('.form-control-feedback', true).first();
10395 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10398 if(this.indicator){
10399 this.indicator.removeClass('visible');
10400 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10407 if(this.allowBlank && !this.getRawValue().length){
10410 if (Roo.bootstrap.version == 3) {
10411 this.el.addClass(this.validClass);
10413 this.inputEl().addClass('is-valid');
10416 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10418 var feedback = this.el.select('.form-control-feedback', true).first();
10421 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10422 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10427 this.fireEvent('valid', this);
10431 * Mark this field as invalid
10432 * @param {String} msg The validation message
10434 markInvalid : function(msg)
10436 if(!this.el || this.preventMark){ // not rendered
10440 this.el.removeClass([this.invalidClass, this.validClass]);
10441 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10443 var feedback = this.el.select('.form-control-feedback', true).first();
10446 this.el.select('.form-control-feedback', true).first().removeClass(
10447 [this.invalidFeedbackClass, this.validFeedbackClass]);
10454 if(this.allowBlank && !this.getRawValue().length){
10458 if(this.indicator){
10459 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10460 this.indicator.addClass('visible');
10462 if (Roo.bootstrap.version == 3) {
10463 this.el.addClass(this.invalidClass);
10465 this.inputEl().addClass('is-invalid');
10470 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10472 var feedback = this.el.select('.form-control-feedback', true).first();
10475 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10477 if(this.getValue().length || this.forceFeedback){
10478 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10485 this.fireEvent('invalid', this, msg);
10488 SafariOnKeyDown : function(event)
10490 // this is a workaround for a password hang bug on chrome/ webkit.
10491 if (this.inputEl().dom.type != 'password') {
10495 var isSelectAll = false;
10497 if(this.inputEl().dom.selectionEnd > 0){
10498 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10500 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10501 event.preventDefault();
10506 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10508 event.preventDefault();
10509 // this is very hacky as keydown always get's upper case.
10511 var cc = String.fromCharCode(event.getCharCode());
10512 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10516 adjustWidth : function(tag, w){
10517 tag = tag.toLowerCase();
10518 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10519 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10520 if(tag == 'input'){
10523 if(tag == 'textarea'){
10526 }else if(Roo.isOpera){
10527 if(tag == 'input'){
10530 if(tag == 'textarea'){
10538 setFieldLabel : function(v)
10540 if(!this.rendered){
10544 if(this.indicatorEl()){
10545 var ar = this.el.select('label > span',true);
10547 if (ar.elements.length) {
10548 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10549 this.fieldLabel = v;
10553 var br = this.el.select('label',true);
10555 if(br.elements.length) {
10556 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10557 this.fieldLabel = v;
10561 Roo.log('Cannot Found any of label > span || label in input');
10565 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10566 this.fieldLabel = v;
10581 * @class Roo.bootstrap.TextArea
10582 * @extends Roo.bootstrap.Input
10583 * Bootstrap TextArea class
10584 * @cfg {Number} cols Specifies the visible width of a text area
10585 * @cfg {Number} rows Specifies the visible number of lines in a text area
10586 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10587 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10588 * @cfg {string} html text
10591 * Create a new TextArea
10592 * @param {Object} config The config object
10595 Roo.bootstrap.TextArea = function(config){
10596 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10600 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10610 getAutoCreate : function(){
10612 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10618 if(this.inputType != 'hidden'){
10619 cfg.cls = 'form-group' //input-group
10627 value : this.value || '',
10628 html: this.html || '',
10629 cls : 'form-control',
10630 placeholder : this.placeholder || ''
10634 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10635 input.maxLength = this.maxLength;
10639 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10643 input.cols = this.cols;
10646 if (this.readOnly) {
10647 input.readonly = true;
10651 input.name = this.name;
10655 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10659 ['xs','sm','md','lg'].map(function(size){
10660 if (settings[size]) {
10661 cfg.cls += ' col-' + size + '-' + settings[size];
10665 var inputblock = input;
10667 if(this.hasFeedback && !this.allowBlank){
10671 cls: 'glyphicon form-control-feedback'
10675 cls : 'has-feedback',
10684 if (this.before || this.after) {
10687 cls : 'input-group',
10691 inputblock.cn.push({
10693 cls : 'input-group-addon',
10698 inputblock.cn.push(input);
10700 if(this.hasFeedback && !this.allowBlank){
10701 inputblock.cls += ' has-feedback';
10702 inputblock.cn.push(feedback);
10706 inputblock.cn.push({
10708 cls : 'input-group-addon',
10715 if (align ==='left' && this.fieldLabel.length) {
10720 cls : 'control-label',
10721 html : this.fieldLabel
10732 if(this.labelWidth > 12){
10733 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10736 if(this.labelWidth < 13 && this.labelmd == 0){
10737 this.labelmd = this.labelWidth;
10740 if(this.labellg > 0){
10741 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10742 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10745 if(this.labelmd > 0){
10746 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10747 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10750 if(this.labelsm > 0){
10751 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10752 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10755 if(this.labelxs > 0){
10756 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10757 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10760 } else if ( this.fieldLabel.length) {
10765 //cls : 'input-group-addon',
10766 html : this.fieldLabel
10784 if (this.disabled) {
10785 input.disabled=true;
10792 * return the real textarea element.
10794 inputEl: function ()
10796 return this.el.select('textarea.form-control',true).first();
10800 * Clear any invalid styles/messages for this field
10802 clearInvalid : function()
10805 if(!this.el || this.preventMark){ // not rendered
10809 var label = this.el.select('label', true).first();
10810 var icon = this.el.select('i.fa-star', true).first();
10815 this.el.removeClass( this.validClass);
10816 this.inputEl().removeClass('is-invalid');
10818 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10820 var feedback = this.el.select('.form-control-feedback', true).first();
10823 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10828 this.fireEvent('valid', this);
10832 * Mark this field as valid
10834 markValid : function()
10836 if(!this.el || this.preventMark){ // not rendered
10840 this.el.removeClass([this.invalidClass, this.validClass]);
10841 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10843 var feedback = this.el.select('.form-control-feedback', true).first();
10846 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10849 if(this.disabled || this.allowBlank){
10853 var label = this.el.select('label', true).first();
10854 var icon = this.el.select('i.fa-star', true).first();
10859 if (Roo.bootstrap.version == 3) {
10860 this.el.addClass(this.validClass);
10862 this.inputEl().addClass('is-valid');
10866 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10868 var feedback = this.el.select('.form-control-feedback', true).first();
10871 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10872 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10877 this.fireEvent('valid', this);
10881 * Mark this field as invalid
10882 * @param {String} msg The validation message
10884 markInvalid : function(msg)
10886 if(!this.el || this.preventMark){ // not rendered
10890 this.el.removeClass([this.invalidClass, this.validClass]);
10891 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10893 var feedback = this.el.select('.form-control-feedback', true).first();
10896 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10899 if(this.disabled || this.allowBlank){
10903 var label = this.el.select('label', true).first();
10904 var icon = this.el.select('i.fa-star', true).first();
10906 if(!this.getValue().length && label && !icon){
10907 this.el.createChild({
10909 cls : 'text-danger fa fa-lg fa-star',
10910 tooltip : 'This field is required',
10911 style : 'margin-right:5px;'
10915 if (Roo.bootstrap.version == 3) {
10916 this.el.addClass(this.invalidClass);
10918 this.inputEl().addClass('is-invalid');
10921 // fixme ... this may be depricated need to test..
10922 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10924 var feedback = this.el.select('.form-control-feedback', true).first();
10927 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10929 if(this.getValue().length || this.forceFeedback){
10930 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10937 this.fireEvent('invalid', this, msg);
10945 * trigger field - base class for combo..
10950 * @class Roo.bootstrap.TriggerField
10951 * @extends Roo.bootstrap.Input
10952 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10953 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10954 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10955 * for which you can provide a custom implementation. For example:
10957 var trigger = new Roo.bootstrap.TriggerField();
10958 trigger.onTriggerClick = myTriggerFn;
10959 trigger.applyTo('my-field');
10962 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10963 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10964 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10965 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10966 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10969 * Create a new TriggerField.
10970 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10971 * to the base TextField)
10973 Roo.bootstrap.TriggerField = function(config){
10974 this.mimicing = false;
10975 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10978 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10980 * @cfg {String} triggerClass A CSS class to apply to the trigger
10983 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10988 * @cfg {Boolean} removable (true|false) special filter default false
10992 /** @cfg {Boolean} grow @hide */
10993 /** @cfg {Number} growMin @hide */
10994 /** @cfg {Number} growMax @hide */
11000 autoSize: Roo.emptyFn,
11004 deferHeight : true,
11007 actionMode : 'wrap',
11012 getAutoCreate : function(){
11014 var align = this.labelAlign || this.parentLabelAlign();
11019 cls: 'form-group' //input-group
11026 type : this.inputType,
11027 cls : 'form-control',
11028 autocomplete: 'new-password',
11029 placeholder : this.placeholder || ''
11033 input.name = this.name;
11036 input.cls += ' input-' + this.size;
11039 if (this.disabled) {
11040 input.disabled=true;
11043 var inputblock = input;
11045 if(this.hasFeedback && !this.allowBlank){
11049 cls: 'glyphicon form-control-feedback'
11052 if(this.removable && !this.editable && !this.tickable){
11054 cls : 'has-feedback',
11060 cls : 'roo-combo-removable-btn close'
11067 cls : 'has-feedback',
11076 if(this.removable && !this.editable && !this.tickable){
11078 cls : 'roo-removable',
11084 cls : 'roo-combo-removable-btn close'
11091 if (this.before || this.after) {
11094 cls : 'input-group',
11098 inputblock.cn.push({
11100 cls : 'input-group-addon input-group-prepend input-group-text',
11105 inputblock.cn.push(input);
11107 if(this.hasFeedback && !this.allowBlank){
11108 inputblock.cls += ' has-feedback';
11109 inputblock.cn.push(feedback);
11113 inputblock.cn.push({
11115 cls : 'input-group-addon input-group-append input-group-text',
11124 var ibwrap = inputblock;
11129 cls: 'roo-select2-choices',
11133 cls: 'roo-select2-search-field',
11145 cls: 'roo-select2-container input-group',
11150 cls: 'form-hidden-field'
11156 if(!this.multiple && this.showToggleBtn){
11162 if (this.caret != false) {
11165 cls: 'fa fa-' + this.caret
11172 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11174 Roo.bootstrap.version == 3 ? caret : '',
11177 cls: 'combobox-clear',
11191 combobox.cls += ' roo-select2-container-multi';
11195 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11196 tooltip : 'This field is required'
11198 if (Roo.bootstrap.version == 4) {
11201 style : 'display:none'
11206 if (align ==='left' && this.fieldLabel.length) {
11208 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11215 cls : 'control-label',
11216 html : this.fieldLabel
11228 var labelCfg = cfg.cn[1];
11229 var contentCfg = cfg.cn[2];
11231 if(this.indicatorpos == 'right'){
11236 cls : 'control-label',
11240 html : this.fieldLabel
11254 labelCfg = cfg.cn[0];
11255 contentCfg = cfg.cn[1];
11258 if(this.labelWidth > 12){
11259 labelCfg.style = "width: " + this.labelWidth + 'px';
11262 if(this.labelWidth < 13 && this.labelmd == 0){
11263 this.labelmd = this.labelWidth;
11266 if(this.labellg > 0){
11267 labelCfg.cls += ' col-lg-' + this.labellg;
11268 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11271 if(this.labelmd > 0){
11272 labelCfg.cls += ' col-md-' + this.labelmd;
11273 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11276 if(this.labelsm > 0){
11277 labelCfg.cls += ' col-sm-' + this.labelsm;
11278 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11281 if(this.labelxs > 0){
11282 labelCfg.cls += ' col-xs-' + this.labelxs;
11283 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11286 } else if ( this.fieldLabel.length) {
11287 // Roo.log(" label");
11292 //cls : 'input-group-addon',
11293 html : this.fieldLabel
11301 if(this.indicatorpos == 'right'){
11309 html : this.fieldLabel
11323 // Roo.log(" no label && no align");
11330 ['xs','sm','md','lg'].map(function(size){
11331 if (settings[size]) {
11332 cfg.cls += ' col-' + size + '-' + settings[size];
11343 onResize : function(w, h){
11344 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11345 // if(typeof w == 'number'){
11346 // var x = w - this.trigger.getWidth();
11347 // this.inputEl().setWidth(this.adjustWidth('input', x));
11348 // this.trigger.setStyle('left', x+'px');
11353 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11356 getResizeEl : function(){
11357 return this.inputEl();
11361 getPositionEl : function(){
11362 return this.inputEl();
11366 alignErrorIcon : function(){
11367 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11371 initEvents : function(){
11375 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11376 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11377 if(!this.multiple && this.showToggleBtn){
11378 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11379 if(this.hideTrigger){
11380 this.trigger.setDisplayed(false);
11382 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11386 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11389 if(this.removable && !this.editable && !this.tickable){
11390 var close = this.closeTriggerEl();
11393 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11394 close.on('click', this.removeBtnClick, this, close);
11398 //this.trigger.addClassOnOver('x-form-trigger-over');
11399 //this.trigger.addClassOnClick('x-form-trigger-click');
11402 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11406 closeTriggerEl : function()
11408 var close = this.el.select('.roo-combo-removable-btn', true).first();
11409 return close ? close : false;
11412 removeBtnClick : function(e, h, el)
11414 e.preventDefault();
11416 if(this.fireEvent("remove", this) !== false){
11418 this.fireEvent("afterremove", this)
11422 createList : function()
11424 this.list = Roo.get(document.body).createChild({
11425 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11426 cls: 'typeahead typeahead-long dropdown-menu',
11427 style: 'display:none'
11430 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11435 initTrigger : function(){
11440 onDestroy : function(){
11442 this.trigger.removeAllListeners();
11443 // this.trigger.remove();
11446 // this.wrap.remove();
11448 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11452 onFocus : function(){
11453 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11455 if(!this.mimicing){
11456 this.wrap.addClass('x-trigger-wrap-focus');
11457 this.mimicing = true;
11458 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11459 if(this.monitorTab){
11460 this.el.on("keydown", this.checkTab, this);
11467 checkTab : function(e){
11468 if(e.getKey() == e.TAB){
11469 this.triggerBlur();
11474 onBlur : function(){
11479 mimicBlur : function(e, t){
11481 if(!this.wrap.contains(t) && this.validateBlur()){
11482 this.triggerBlur();
11488 triggerBlur : function(){
11489 this.mimicing = false;
11490 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11491 if(this.monitorTab){
11492 this.el.un("keydown", this.checkTab, this);
11494 //this.wrap.removeClass('x-trigger-wrap-focus');
11495 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11499 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11500 validateBlur : function(e, t){
11505 onDisable : function(){
11506 this.inputEl().dom.disabled = true;
11507 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11509 // this.wrap.addClass('x-item-disabled');
11514 onEnable : function(){
11515 this.inputEl().dom.disabled = false;
11516 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11518 // this.el.removeClass('x-item-disabled');
11523 onShow : function(){
11524 var ae = this.getActionEl();
11527 ae.dom.style.display = '';
11528 ae.dom.style.visibility = 'visible';
11534 onHide : function(){
11535 var ae = this.getActionEl();
11536 ae.dom.style.display = 'none';
11540 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11541 * by an implementing function.
11543 * @param {EventObject} e
11545 onTriggerClick : Roo.emptyFn
11549 * Ext JS Library 1.1.1
11550 * Copyright(c) 2006-2007, Ext JS, LLC.
11552 * Originally Released Under LGPL - original licence link has changed is not relivant.
11555 * <script type="text/javascript">
11560 * @class Roo.data.SortTypes
11562 * Defines the default sorting (casting?) comparison functions used when sorting data.
11564 Roo.data.SortTypes = {
11566 * Default sort that does nothing
11567 * @param {Mixed} s The value being converted
11568 * @return {Mixed} The comparison value
11570 none : function(s){
11575 * The regular expression used to strip tags
11579 stripTagsRE : /<\/?[^>]+>/gi,
11582 * Strips all HTML tags to sort on text only
11583 * @param {Mixed} s The value being converted
11584 * @return {String} The comparison value
11586 asText : function(s){
11587 return String(s).replace(this.stripTagsRE, "");
11591 * Strips all HTML tags to sort on text only - Case insensitive
11592 * @param {Mixed} s The value being converted
11593 * @return {String} The comparison value
11595 asUCText : function(s){
11596 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11600 * Case insensitive string
11601 * @param {Mixed} s The value being converted
11602 * @return {String} The comparison value
11604 asUCString : function(s) {
11605 return String(s).toUpperCase();
11610 * @param {Mixed} s The value being converted
11611 * @return {Number} The comparison value
11613 asDate : function(s) {
11617 if(s instanceof Date){
11618 return s.getTime();
11620 return Date.parse(String(s));
11625 * @param {Mixed} s The value being converted
11626 * @return {Float} The comparison value
11628 asFloat : function(s) {
11629 var val = parseFloat(String(s).replace(/,/g, ""));
11638 * @param {Mixed} s The value being converted
11639 * @return {Number} The comparison value
11641 asInt : function(s) {
11642 var val = parseInt(String(s).replace(/,/g, ""));
11650 * Ext JS Library 1.1.1
11651 * Copyright(c) 2006-2007, Ext JS, LLC.
11653 * Originally Released Under LGPL - original licence link has changed is not relivant.
11656 * <script type="text/javascript">
11660 * @class Roo.data.Record
11661 * Instances of this class encapsulate both record <em>definition</em> information, and record
11662 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11663 * to access Records cached in an {@link Roo.data.Store} object.<br>
11665 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11666 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11669 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11671 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11672 * {@link #create}. The parameters are the same.
11673 * @param {Array} data An associative Array of data values keyed by the field name.
11674 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11675 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11676 * not specified an integer id is generated.
11678 Roo.data.Record = function(data, id){
11679 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11684 * Generate a constructor for a specific record layout.
11685 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11686 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11687 * Each field definition object may contain the following properties: <ul>
11688 * <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,
11689 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11690 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11691 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11692 * is being used, then this is a string containing the javascript expression to reference the data relative to
11693 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11694 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11695 * this may be omitted.</p></li>
11696 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11697 * <ul><li>auto (Default, implies no conversion)</li>
11702 * <li>date</li></ul></p></li>
11703 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11704 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11705 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11706 * by the Reader into an object that will be stored in the Record. It is passed the
11707 * following parameters:<ul>
11708 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11710 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11712 * <br>usage:<br><pre><code>
11713 var TopicRecord = Roo.data.Record.create(
11714 {name: 'title', mapping: 'topic_title'},
11715 {name: 'author', mapping: 'username'},
11716 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11717 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11718 {name: 'lastPoster', mapping: 'user2'},
11719 {name: 'excerpt', mapping: 'post_text'}
11722 var myNewRecord = new TopicRecord({
11723 title: 'Do my job please',
11726 lastPost: new Date(),
11727 lastPoster: 'Animal',
11728 excerpt: 'No way dude!'
11730 myStore.add(myNewRecord);
11735 Roo.data.Record.create = function(o){
11736 var f = function(){
11737 f.superclass.constructor.apply(this, arguments);
11739 Roo.extend(f, Roo.data.Record);
11740 var p = f.prototype;
11741 p.fields = new Roo.util.MixedCollection(false, function(field){
11744 for(var i = 0, len = o.length; i < len; i++){
11745 p.fields.add(new Roo.data.Field(o[i]));
11747 f.getField = function(name){
11748 return p.fields.get(name);
11753 Roo.data.Record.AUTO_ID = 1000;
11754 Roo.data.Record.EDIT = 'edit';
11755 Roo.data.Record.REJECT = 'reject';
11756 Roo.data.Record.COMMIT = 'commit';
11758 Roo.data.Record.prototype = {
11760 * Readonly flag - true if this record has been modified.
11769 join : function(store){
11770 this.store = store;
11774 * Set the named field to the specified value.
11775 * @param {String} name The name of the field to set.
11776 * @param {Object} value The value to set the field to.
11778 set : function(name, value){
11779 if(this.data[name] == value){
11783 if(!this.modified){
11784 this.modified = {};
11786 if(typeof this.modified[name] == 'undefined'){
11787 this.modified[name] = this.data[name];
11789 this.data[name] = value;
11790 if(!this.editing && this.store){
11791 this.store.afterEdit(this);
11796 * Get the value of the named field.
11797 * @param {String} name The name of the field to get the value of.
11798 * @return {Object} The value of the field.
11800 get : function(name){
11801 return this.data[name];
11805 beginEdit : function(){
11806 this.editing = true;
11807 this.modified = {};
11811 cancelEdit : function(){
11812 this.editing = false;
11813 delete this.modified;
11817 endEdit : function(){
11818 this.editing = false;
11819 if(this.dirty && this.store){
11820 this.store.afterEdit(this);
11825 * Usually called by the {@link Roo.data.Store} which owns the Record.
11826 * Rejects all changes made to the Record since either creation, or the last commit operation.
11827 * Modified fields are reverted to their original values.
11829 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11830 * of reject operations.
11832 reject : function(){
11833 var m = this.modified;
11835 if(typeof m[n] != "function"){
11836 this.data[n] = m[n];
11839 this.dirty = false;
11840 delete this.modified;
11841 this.editing = false;
11843 this.store.afterReject(this);
11848 * Usually called by the {@link Roo.data.Store} which owns the Record.
11849 * Commits all changes made to the Record since either creation, or the last commit operation.
11851 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11852 * of commit operations.
11854 commit : function(){
11855 this.dirty = false;
11856 delete this.modified;
11857 this.editing = false;
11859 this.store.afterCommit(this);
11864 hasError : function(){
11865 return this.error != null;
11869 clearError : function(){
11874 * Creates a copy of this record.
11875 * @param {String} id (optional) A new record id if you don't want to use this record's id
11878 copy : function(newId) {
11879 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11883 * Ext JS Library 1.1.1
11884 * Copyright(c) 2006-2007, Ext JS, LLC.
11886 * Originally Released Under LGPL - original licence link has changed is not relivant.
11889 * <script type="text/javascript">
11895 * @class Roo.data.Store
11896 * @extends Roo.util.Observable
11897 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11898 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11900 * 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
11901 * has no knowledge of the format of the data returned by the Proxy.<br>
11903 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11904 * instances from the data object. These records are cached and made available through accessor functions.
11906 * Creates a new Store.
11907 * @param {Object} config A config object containing the objects needed for the Store to access data,
11908 * and read the data into Records.
11910 Roo.data.Store = function(config){
11911 this.data = new Roo.util.MixedCollection(false);
11912 this.data.getKey = function(o){
11915 this.baseParams = {};
11917 this.paramNames = {
11922 "multisort" : "_multisort"
11925 if(config && config.data){
11926 this.inlineData = config.data;
11927 delete config.data;
11930 Roo.apply(this, config);
11932 if(this.reader){ // reader passed
11933 this.reader = Roo.factory(this.reader, Roo.data);
11934 this.reader.xmodule = this.xmodule || false;
11935 if(!this.recordType){
11936 this.recordType = this.reader.recordType;
11938 if(this.reader.onMetaChange){
11939 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11943 if(this.recordType){
11944 this.fields = this.recordType.prototype.fields;
11946 this.modified = [];
11950 * @event datachanged
11951 * Fires when the data cache has changed, and a widget which is using this Store
11952 * as a Record cache should refresh its view.
11953 * @param {Store} this
11955 datachanged : true,
11957 * @event metachange
11958 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11959 * @param {Store} this
11960 * @param {Object} meta The JSON metadata
11965 * Fires when Records have been added to the Store
11966 * @param {Store} this
11967 * @param {Roo.data.Record[]} records The array of Records added
11968 * @param {Number} index The index at which the record(s) were added
11973 * Fires when a Record has been removed from the Store
11974 * @param {Store} this
11975 * @param {Roo.data.Record} record The Record that was removed
11976 * @param {Number} index The index at which the record was removed
11981 * Fires when a Record has been updated
11982 * @param {Store} this
11983 * @param {Roo.data.Record} record The Record that was updated
11984 * @param {String} operation The update operation being performed. Value may be one of:
11986 Roo.data.Record.EDIT
11987 Roo.data.Record.REJECT
11988 Roo.data.Record.COMMIT
11994 * Fires when the data cache has been cleared.
11995 * @param {Store} this
11999 * @event beforeload
12000 * Fires before a request is made for a new data object. If the beforeload handler returns false
12001 * the load action will be canceled.
12002 * @param {Store} this
12003 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12007 * @event beforeloadadd
12008 * Fires after a new set of Records has been loaded.
12009 * @param {Store} this
12010 * @param {Roo.data.Record[]} records The Records that were loaded
12011 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12013 beforeloadadd : true,
12016 * Fires after a new set of Records has been loaded, before they are added to the store.
12017 * @param {Store} this
12018 * @param {Roo.data.Record[]} records The Records that were loaded
12019 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12020 * @params {Object} return from reader
12024 * @event loadexception
12025 * Fires if an exception occurs in the Proxy during loading.
12026 * Called with the signature of the Proxy's "loadexception" event.
12027 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12030 * @param {Object} return from JsonData.reader() - success, totalRecords, records
12031 * @param {Object} load options
12032 * @param {Object} jsonData from your request (normally this contains the Exception)
12034 loadexception : true
12038 this.proxy = Roo.factory(this.proxy, Roo.data);
12039 this.proxy.xmodule = this.xmodule || false;
12040 this.relayEvents(this.proxy, ["loadexception"]);
12042 this.sortToggle = {};
12043 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12045 Roo.data.Store.superclass.constructor.call(this);
12047 if(this.inlineData){
12048 this.loadData(this.inlineData);
12049 delete this.inlineData;
12053 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12055 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
12056 * without a remote query - used by combo/forms at present.
12060 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12063 * @cfg {Array} data Inline data to be loaded when the store is initialized.
12066 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12067 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12070 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12071 * on any HTTP request
12074 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12077 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12081 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12082 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12084 remoteSort : false,
12087 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12088 * loaded or when a record is removed. (defaults to false).
12090 pruneModifiedRecords : false,
12093 lastOptions : null,
12096 * Add Records to the Store and fires the add event.
12097 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12099 add : function(records){
12100 records = [].concat(records);
12101 for(var i = 0, len = records.length; i < len; i++){
12102 records[i].join(this);
12104 var index = this.data.length;
12105 this.data.addAll(records);
12106 this.fireEvent("add", this, records, index);
12110 * Remove a Record from the Store and fires the remove event.
12111 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12113 remove : function(record){
12114 var index = this.data.indexOf(record);
12115 this.data.removeAt(index);
12117 if(this.pruneModifiedRecords){
12118 this.modified.remove(record);
12120 this.fireEvent("remove", this, record, index);
12124 * Remove all Records from the Store and fires the clear event.
12126 removeAll : function(){
12128 if(this.pruneModifiedRecords){
12129 this.modified = [];
12131 this.fireEvent("clear", this);
12135 * Inserts Records to the Store at the given index and fires the add event.
12136 * @param {Number} index The start index at which to insert the passed Records.
12137 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12139 insert : function(index, records){
12140 records = [].concat(records);
12141 for(var i = 0, len = records.length; i < len; i++){
12142 this.data.insert(index, records[i]);
12143 records[i].join(this);
12145 this.fireEvent("add", this, records, index);
12149 * Get the index within the cache of the passed Record.
12150 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12151 * @return {Number} The index of the passed Record. Returns -1 if not found.
12153 indexOf : function(record){
12154 return this.data.indexOf(record);
12158 * Get the index within the cache of the Record with the passed id.
12159 * @param {String} id The id of the Record to find.
12160 * @return {Number} The index of the Record. Returns -1 if not found.
12162 indexOfId : function(id){
12163 return this.data.indexOfKey(id);
12167 * Get the Record with the specified id.
12168 * @param {String} id The id of the Record to find.
12169 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12171 getById : function(id){
12172 return this.data.key(id);
12176 * Get the Record at the specified index.
12177 * @param {Number} index The index of the Record to find.
12178 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12180 getAt : function(index){
12181 return this.data.itemAt(index);
12185 * Returns a range of Records between specified indices.
12186 * @param {Number} startIndex (optional) The starting index (defaults to 0)
12187 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12188 * @return {Roo.data.Record[]} An array of Records
12190 getRange : function(start, end){
12191 return this.data.getRange(start, end);
12195 storeOptions : function(o){
12196 o = Roo.apply({}, o);
12199 this.lastOptions = o;
12203 * Loads the Record cache from the configured Proxy using the configured Reader.
12205 * If using remote paging, then the first load call must specify the <em>start</em>
12206 * and <em>limit</em> properties in the options.params property to establish the initial
12207 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12209 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12210 * and this call will return before the new data has been loaded. Perform any post-processing
12211 * in a callback function, or in a "load" event handler.</strong>
12213 * @param {Object} options An object containing properties which control loading options:<ul>
12214 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12215 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12216 * passed the following arguments:<ul>
12217 * <li>r : Roo.data.Record[]</li>
12218 * <li>options: Options object from the load call</li>
12219 * <li>success: Boolean success indicator</li></ul></li>
12220 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12221 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12224 load : function(options){
12225 options = options || {};
12226 if(this.fireEvent("beforeload", this, options) !== false){
12227 this.storeOptions(options);
12228 var p = Roo.apply(options.params || {}, this.baseParams);
12229 // if meta was not loaded from remote source.. try requesting it.
12230 if (!this.reader.metaFromRemote) {
12231 p._requestMeta = 1;
12233 if(this.sortInfo && this.remoteSort){
12234 var pn = this.paramNames;
12235 p[pn["sort"]] = this.sortInfo.field;
12236 p[pn["dir"]] = this.sortInfo.direction;
12238 if (this.multiSort) {
12239 var pn = this.paramNames;
12240 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12243 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12248 * Reloads the Record cache from the configured Proxy using the configured Reader and
12249 * the options from the last load operation performed.
12250 * @param {Object} options (optional) An object containing properties which may override the options
12251 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12252 * the most recently used options are reused).
12254 reload : function(options){
12255 this.load(Roo.applyIf(options||{}, this.lastOptions));
12259 // Called as a callback by the Reader during a load operation.
12260 loadRecords : function(o, options, success){
12261 if(!o || success === false){
12262 if(success !== false){
12263 this.fireEvent("load", this, [], options, o);
12265 if(options.callback){
12266 options.callback.call(options.scope || this, [], options, false);
12270 // if data returned failure - throw an exception.
12271 if (o.success === false) {
12272 // show a message if no listener is registered.
12273 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12274 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12276 // loadmask wil be hooked into this..
12277 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12280 var r = o.records, t = o.totalRecords || r.length;
12282 this.fireEvent("beforeloadadd", this, r, options, o);
12284 if(!options || options.add !== true){
12285 if(this.pruneModifiedRecords){
12286 this.modified = [];
12288 for(var i = 0, len = r.length; i < len; i++){
12292 this.data = this.snapshot;
12293 delete this.snapshot;
12296 this.data.addAll(r);
12297 this.totalLength = t;
12299 this.fireEvent("datachanged", this);
12301 this.totalLength = Math.max(t, this.data.length+r.length);
12305 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12307 var e = new Roo.data.Record({});
12309 e.set(this.parent.displayField, this.parent.emptyTitle);
12310 e.set(this.parent.valueField, '');
12315 this.fireEvent("load", this, r, options, o);
12316 if(options.callback){
12317 options.callback.call(options.scope || this, r, options, true);
12323 * Loads data from a passed data block. A Reader which understands the format of the data
12324 * must have been configured in the constructor.
12325 * @param {Object} data The data block from which to read the Records. The format of the data expected
12326 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12327 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12329 loadData : function(o, append){
12330 var r = this.reader.readRecords(o);
12331 this.loadRecords(r, {add: append}, true);
12335 * using 'cn' the nested child reader read the child array into it's child stores.
12336 * @param {Object} rec The record with a 'children array
12338 loadDataFromChildren : function(rec)
12340 this.loadData(this.reader.toLoadData(rec));
12345 * Gets the number of cached records.
12347 * <em>If using paging, this may not be the total size of the dataset. If the data object
12348 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12349 * the data set size</em>
12351 getCount : function(){
12352 return this.data.length || 0;
12356 * Gets the total number of records in the dataset as returned by the server.
12358 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12359 * the dataset size</em>
12361 getTotalCount : function(){
12362 return this.totalLength || 0;
12366 * Returns the sort state of the Store as an object with two properties:
12368 field {String} The name of the field by which the Records are sorted
12369 direction {String} The sort order, "ASC" or "DESC"
12372 getSortState : function(){
12373 return this.sortInfo;
12377 applySort : function(){
12378 if(this.sortInfo && !this.remoteSort){
12379 var s = this.sortInfo, f = s.field;
12380 var st = this.fields.get(f).sortType;
12381 var fn = function(r1, r2){
12382 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12383 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12385 this.data.sort(s.direction, fn);
12386 if(this.snapshot && this.snapshot != this.data){
12387 this.snapshot.sort(s.direction, fn);
12393 * Sets the default sort column and order to be used by the next load operation.
12394 * @param {String} fieldName The name of the field to sort by.
12395 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12397 setDefaultSort : function(field, dir){
12398 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12402 * Sort the Records.
12403 * If remote sorting is used, the sort is performed on the server, and the cache is
12404 * reloaded. If local sorting is used, the cache is sorted internally.
12405 * @param {String} fieldName The name of the field to sort by.
12406 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12408 sort : function(fieldName, dir){
12409 var f = this.fields.get(fieldName);
12411 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12413 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12414 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12419 this.sortToggle[f.name] = dir;
12420 this.sortInfo = {field: f.name, direction: dir};
12421 if(!this.remoteSort){
12423 this.fireEvent("datachanged", this);
12425 this.load(this.lastOptions);
12430 * Calls the specified function for each of the Records in the cache.
12431 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12432 * Returning <em>false</em> aborts and exits the iteration.
12433 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12435 each : function(fn, scope){
12436 this.data.each(fn, scope);
12440 * Gets all records modified since the last commit. Modified records are persisted across load operations
12441 * (e.g., during paging).
12442 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12444 getModifiedRecords : function(){
12445 return this.modified;
12449 createFilterFn : function(property, value, anyMatch){
12450 if(!value.exec){ // not a regex
12451 value = String(value);
12452 if(value.length == 0){
12455 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12457 return function(r){
12458 return value.test(r.data[property]);
12463 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12464 * @param {String} property A field on your records
12465 * @param {Number} start The record index to start at (defaults to 0)
12466 * @param {Number} end The last record index to include (defaults to length - 1)
12467 * @return {Number} The sum
12469 sum : function(property, start, end){
12470 var rs = this.data.items, v = 0;
12471 start = start || 0;
12472 end = (end || end === 0) ? end : rs.length-1;
12474 for(var i = start; i <= end; i++){
12475 v += (rs[i].data[property] || 0);
12481 * Filter the records by a specified property.
12482 * @param {String} field A field on your records
12483 * @param {String/RegExp} value Either a string that the field
12484 * should start with or a RegExp to test against the field
12485 * @param {Boolean} anyMatch True to match any part not just the beginning
12487 filter : function(property, value, anyMatch){
12488 var fn = this.createFilterFn(property, value, anyMatch);
12489 return fn ? this.filterBy(fn) : this.clearFilter();
12493 * Filter by a function. The specified function will be called with each
12494 * record in this data source. If the function returns true the record is included,
12495 * otherwise it is filtered.
12496 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12497 * @param {Object} scope (optional) The scope of the function (defaults to this)
12499 filterBy : function(fn, scope){
12500 this.snapshot = this.snapshot || this.data;
12501 this.data = this.queryBy(fn, scope||this);
12502 this.fireEvent("datachanged", this);
12506 * Query the records by a specified property.
12507 * @param {String} field A field on your records
12508 * @param {String/RegExp} value Either a string that the field
12509 * should start with or a RegExp to test against the field
12510 * @param {Boolean} anyMatch True to match any part not just the beginning
12511 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12513 query : function(property, value, anyMatch){
12514 var fn = this.createFilterFn(property, value, anyMatch);
12515 return fn ? this.queryBy(fn) : this.data.clone();
12519 * Query by a function. The specified function will be called with each
12520 * record in this data source. If the function returns true the record is included
12522 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12523 * @param {Object} scope (optional) The scope of the function (defaults to this)
12524 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12526 queryBy : function(fn, scope){
12527 var data = this.snapshot || this.data;
12528 return data.filterBy(fn, scope||this);
12532 * Collects unique values for a particular dataIndex from this store.
12533 * @param {String} dataIndex The property to collect
12534 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12535 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12536 * @return {Array} An array of the unique values
12538 collect : function(dataIndex, allowNull, bypassFilter){
12539 var d = (bypassFilter === true && this.snapshot) ?
12540 this.snapshot.items : this.data.items;
12541 var v, sv, r = [], l = {};
12542 for(var i = 0, len = d.length; i < len; i++){
12543 v = d[i].data[dataIndex];
12545 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12554 * Revert to a view of the Record cache with no filtering applied.
12555 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12557 clearFilter : function(suppressEvent){
12558 if(this.snapshot && this.snapshot != this.data){
12559 this.data = this.snapshot;
12560 delete this.snapshot;
12561 if(suppressEvent !== true){
12562 this.fireEvent("datachanged", this);
12568 afterEdit : function(record){
12569 if(this.modified.indexOf(record) == -1){
12570 this.modified.push(record);
12572 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12576 afterReject : function(record){
12577 this.modified.remove(record);
12578 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12582 afterCommit : function(record){
12583 this.modified.remove(record);
12584 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12588 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12589 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12591 commitChanges : function(){
12592 var m = this.modified.slice(0);
12593 this.modified = [];
12594 for(var i = 0, len = m.length; i < len; i++){
12600 * Cancel outstanding changes on all changed records.
12602 rejectChanges : function(){
12603 var m = this.modified.slice(0);
12604 this.modified = [];
12605 for(var i = 0, len = m.length; i < len; i++){
12610 onMetaChange : function(meta, rtype, o){
12611 this.recordType = rtype;
12612 this.fields = rtype.prototype.fields;
12613 delete this.snapshot;
12614 this.sortInfo = meta.sortInfo || this.sortInfo;
12615 this.modified = [];
12616 this.fireEvent('metachange', this, this.reader.meta);
12619 moveIndex : function(data, type)
12621 var index = this.indexOf(data);
12623 var newIndex = index + type;
12627 this.insert(newIndex, data);
12632 * Ext JS Library 1.1.1
12633 * Copyright(c) 2006-2007, Ext JS, LLC.
12635 * Originally Released Under LGPL - original licence link has changed is not relivant.
12638 * <script type="text/javascript">
12642 * @class Roo.data.SimpleStore
12643 * @extends Roo.data.Store
12644 * Small helper class to make creating Stores from Array data easier.
12645 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12646 * @cfg {Array} fields An array of field definition objects, or field name strings.
12647 * @cfg {Object} an existing reader (eg. copied from another store)
12648 * @cfg {Array} data The multi-dimensional array of data
12650 * @param {Object} config
12652 Roo.data.SimpleStore = function(config)
12654 Roo.data.SimpleStore.superclass.constructor.call(this, {
12656 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12659 Roo.data.Record.create(config.fields)
12661 proxy : new Roo.data.MemoryProxy(config.data)
12665 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12667 * Ext JS Library 1.1.1
12668 * Copyright(c) 2006-2007, Ext JS, LLC.
12670 * Originally Released Under LGPL - original licence link has changed is not relivant.
12673 * <script type="text/javascript">
12678 * @extends Roo.data.Store
12679 * @class Roo.data.JsonStore
12680 * Small helper class to make creating Stores for JSON data easier. <br/>
12682 var store = new Roo.data.JsonStore({
12683 url: 'get-images.php',
12685 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12688 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12689 * JsonReader and HttpProxy (unless inline data is provided).</b>
12690 * @cfg {Array} fields An array of field definition objects, or field name strings.
12692 * @param {Object} config
12694 Roo.data.JsonStore = function(c){
12695 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12696 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12697 reader: new Roo.data.JsonReader(c, c.fields)
12700 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12702 * Ext JS Library 1.1.1
12703 * Copyright(c) 2006-2007, Ext JS, LLC.
12705 * Originally Released Under LGPL - original licence link has changed is not relivant.
12708 * <script type="text/javascript">
12712 Roo.data.Field = function(config){
12713 if(typeof config == "string"){
12714 config = {name: config};
12716 Roo.apply(this, config);
12719 this.type = "auto";
12722 var st = Roo.data.SortTypes;
12723 // named sortTypes are supported, here we look them up
12724 if(typeof this.sortType == "string"){
12725 this.sortType = st[this.sortType];
12728 // set default sortType for strings and dates
12729 if(!this.sortType){
12732 this.sortType = st.asUCString;
12735 this.sortType = st.asDate;
12738 this.sortType = st.none;
12743 var stripRe = /[\$,%]/g;
12745 // prebuilt conversion function for this field, instead of
12746 // switching every time we're reading a value
12748 var cv, dateFormat = this.dateFormat;
12753 cv = function(v){ return v; };
12756 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12760 return v !== undefined && v !== null && v !== '' ?
12761 parseInt(String(v).replace(stripRe, ""), 10) : '';
12766 return v !== undefined && v !== null && v !== '' ?
12767 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12772 cv = function(v){ return v === true || v === "true" || v == 1; };
12779 if(v instanceof Date){
12783 if(dateFormat == "timestamp"){
12784 return new Date(v*1000);
12786 return Date.parseDate(v, dateFormat);
12788 var parsed = Date.parse(v);
12789 return parsed ? new Date(parsed) : null;
12798 Roo.data.Field.prototype = {
12806 * Ext JS Library 1.1.1
12807 * Copyright(c) 2006-2007, Ext JS, LLC.
12809 * Originally Released Under LGPL - original licence link has changed is not relivant.
12812 * <script type="text/javascript">
12815 // Base class for reading structured data from a data source. This class is intended to be
12816 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12819 * @class Roo.data.DataReader
12820 * Base class for reading structured data from a data source. This class is intended to be
12821 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12824 Roo.data.DataReader = function(meta, recordType){
12828 this.recordType = recordType instanceof Array ?
12829 Roo.data.Record.create(recordType) : recordType;
12832 Roo.data.DataReader.prototype = {
12835 readerType : 'Data',
12837 * Create an empty record
12838 * @param {Object} data (optional) - overlay some values
12839 * @return {Roo.data.Record} record created.
12841 newRow : function(d) {
12843 this.recordType.prototype.fields.each(function(c) {
12845 case 'int' : da[c.name] = 0; break;
12846 case 'date' : da[c.name] = new Date(); break;
12847 case 'float' : da[c.name] = 0.0; break;
12848 case 'boolean' : da[c.name] = false; break;
12849 default : da[c.name] = ""; break;
12853 return new this.recordType(Roo.apply(da, d));
12859 * Ext JS Library 1.1.1
12860 * Copyright(c) 2006-2007, Ext JS, LLC.
12862 * Originally Released Under LGPL - original licence link has changed is not relivant.
12865 * <script type="text/javascript">
12869 * @class Roo.data.DataProxy
12870 * @extends Roo.data.Observable
12871 * This class is an abstract base class for implementations which provide retrieval of
12872 * unformatted data objects.<br>
12874 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12875 * (of the appropriate type which knows how to parse the data object) to provide a block of
12876 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12878 * Custom implementations must implement the load method as described in
12879 * {@link Roo.data.HttpProxy#load}.
12881 Roo.data.DataProxy = function(){
12884 * @event beforeload
12885 * Fires before a network request is made to retrieve a data object.
12886 * @param {Object} This DataProxy object.
12887 * @param {Object} params The params parameter to the load function.
12892 * Fires before the load method's callback is called.
12893 * @param {Object} This DataProxy object.
12894 * @param {Object} o The data object.
12895 * @param {Object} arg The callback argument object passed to the load function.
12899 * @event loadexception
12900 * Fires if an Exception occurs during data retrieval.
12901 * @param {Object} This DataProxy object.
12902 * @param {Object} o The data object.
12903 * @param {Object} arg The callback argument object passed to the load function.
12904 * @param {Object} e The Exception.
12906 loadexception : true
12908 Roo.data.DataProxy.superclass.constructor.call(this);
12911 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12914 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12918 * Ext JS Library 1.1.1
12919 * Copyright(c) 2006-2007, Ext JS, LLC.
12921 * Originally Released Under LGPL - original licence link has changed is not relivant.
12924 * <script type="text/javascript">
12927 * @class Roo.data.MemoryProxy
12928 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12929 * to the Reader when its load method is called.
12931 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12933 Roo.data.MemoryProxy = function(data){
12937 Roo.data.MemoryProxy.superclass.constructor.call(this);
12941 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12944 * Load data from the requested source (in this case an in-memory
12945 * data object passed to the constructor), read the data object into
12946 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12947 * process that block using the passed callback.
12948 * @param {Object} params This parameter is not used by the MemoryProxy class.
12949 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12950 * object into a block of Roo.data.Records.
12951 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12952 * The function must be passed <ul>
12953 * <li>The Record block object</li>
12954 * <li>The "arg" argument from the load function</li>
12955 * <li>A boolean success indicator</li>
12957 * @param {Object} scope The scope in which to call the callback
12958 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12960 load : function(params, reader, callback, scope, arg){
12961 params = params || {};
12964 result = reader.readRecords(params.data ? params.data :this.data);
12966 this.fireEvent("loadexception", this, arg, null, e);
12967 callback.call(scope, null, arg, false);
12970 callback.call(scope, result, arg, true);
12974 update : function(params, records){
12979 * Ext JS Library 1.1.1
12980 * Copyright(c) 2006-2007, Ext JS, LLC.
12982 * Originally Released Under LGPL - original licence link has changed is not relivant.
12985 * <script type="text/javascript">
12988 * @class Roo.data.HttpProxy
12989 * @extends Roo.data.DataProxy
12990 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12991 * configured to reference a certain URL.<br><br>
12993 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12994 * from which the running page was served.<br><br>
12996 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12998 * Be aware that to enable the browser to parse an XML document, the server must set
12999 * the Content-Type header in the HTTP response to "text/xml".
13001 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13002 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
13003 * will be used to make the request.
13005 Roo.data.HttpProxy = function(conn){
13006 Roo.data.HttpProxy.superclass.constructor.call(this);
13007 // is conn a conn config or a real conn?
13009 this.useAjax = !conn || !conn.events;
13013 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13014 // thse are take from connection...
13017 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13020 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13021 * extra parameters to each request made by this object. (defaults to undefined)
13024 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13025 * to each request made by this object. (defaults to undefined)
13028 * @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)
13031 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13034 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13040 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13044 * Return the {@link Roo.data.Connection} object being used by this Proxy.
13045 * @return {Connection} The Connection object. This object may be used to subscribe to events on
13046 * a finer-grained basis than the DataProxy events.
13048 getConnection : function(){
13049 return this.useAjax ? Roo.Ajax : this.conn;
13053 * Load data from the configured {@link Roo.data.Connection}, read the data object into
13054 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13055 * process that block using the passed callback.
13056 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13057 * for the request to the remote server.
13058 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13059 * object into a block of Roo.data.Records.
13060 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13061 * The function must be passed <ul>
13062 * <li>The Record block object</li>
13063 * <li>The "arg" argument from the load function</li>
13064 * <li>A boolean success indicator</li>
13066 * @param {Object} scope The scope in which to call the callback
13067 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13069 load : function(params, reader, callback, scope, arg){
13070 if(this.fireEvent("beforeload", this, params) !== false){
13072 params : params || {},
13074 callback : callback,
13079 callback : this.loadResponse,
13083 Roo.applyIf(o, this.conn);
13084 if(this.activeRequest){
13085 Roo.Ajax.abort(this.activeRequest);
13087 this.activeRequest = Roo.Ajax.request(o);
13089 this.conn.request(o);
13092 callback.call(scope||this, null, arg, false);
13097 loadResponse : function(o, success, response){
13098 delete this.activeRequest;
13100 this.fireEvent("loadexception", this, o, response);
13101 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13106 result = o.reader.read(response);
13108 this.fireEvent("loadexception", this, o, response, e);
13109 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13113 this.fireEvent("load", this, o, o.request.arg);
13114 o.request.callback.call(o.request.scope, result, o.request.arg, true);
13118 update : function(dataSet){
13123 updateResponse : function(dataSet){
13128 * Ext JS Library 1.1.1
13129 * Copyright(c) 2006-2007, Ext JS, LLC.
13131 * Originally Released Under LGPL - original licence link has changed is not relivant.
13134 * <script type="text/javascript">
13138 * @class Roo.data.ScriptTagProxy
13139 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13140 * other than the originating domain of the running page.<br><br>
13142 * <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
13143 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13145 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13146 * source code that is used as the source inside a <script> tag.<br><br>
13148 * In order for the browser to process the returned data, the server must wrap the data object
13149 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13150 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13151 * depending on whether the callback name was passed:
13154 boolean scriptTag = false;
13155 String cb = request.getParameter("callback");
13158 response.setContentType("text/javascript");
13160 response.setContentType("application/x-json");
13162 Writer out = response.getWriter();
13164 out.write(cb + "(");
13166 out.print(dataBlock.toJsonString());
13173 * @param {Object} config A configuration object.
13175 Roo.data.ScriptTagProxy = function(config){
13176 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13177 Roo.apply(this, config);
13178 this.head = document.getElementsByTagName("head")[0];
13181 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13183 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13185 * @cfg {String} url The URL from which to request the data object.
13188 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13192 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13193 * the server the name of the callback function set up by the load call to process the returned data object.
13194 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13195 * javascript output which calls this named function passing the data object as its only parameter.
13197 callbackParam : "callback",
13199 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13200 * name to the request.
13205 * Load data from the configured URL, read the data object into
13206 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13207 * process that block using the passed callback.
13208 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13209 * for the request to the remote server.
13210 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13211 * object into a block of Roo.data.Records.
13212 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13213 * The function must be passed <ul>
13214 * <li>The Record block object</li>
13215 * <li>The "arg" argument from the load function</li>
13216 * <li>A boolean success indicator</li>
13218 * @param {Object} scope The scope in which to call the callback
13219 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13221 load : function(params, reader, callback, scope, arg){
13222 if(this.fireEvent("beforeload", this, params) !== false){
13224 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13226 var url = this.url;
13227 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13229 url += "&_dc=" + (new Date().getTime());
13231 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13234 cb : "stcCallback"+transId,
13235 scriptId : "stcScript"+transId,
13239 callback : callback,
13245 window[trans.cb] = function(o){
13246 conn.handleResponse(o, trans);
13249 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13251 if(this.autoAbort !== false){
13255 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13257 var script = document.createElement("script");
13258 script.setAttribute("src", url);
13259 script.setAttribute("type", "text/javascript");
13260 script.setAttribute("id", trans.scriptId);
13261 this.head.appendChild(script);
13263 this.trans = trans;
13265 callback.call(scope||this, null, arg, false);
13270 isLoading : function(){
13271 return this.trans ? true : false;
13275 * Abort the current server request.
13277 abort : function(){
13278 if(this.isLoading()){
13279 this.destroyTrans(this.trans);
13284 destroyTrans : function(trans, isLoaded){
13285 this.head.removeChild(document.getElementById(trans.scriptId));
13286 clearTimeout(trans.timeoutId);
13288 window[trans.cb] = undefined;
13290 delete window[trans.cb];
13293 // if hasn't been loaded, wait for load to remove it to prevent script error
13294 window[trans.cb] = function(){
13295 window[trans.cb] = undefined;
13297 delete window[trans.cb];
13304 handleResponse : function(o, trans){
13305 this.trans = false;
13306 this.destroyTrans(trans, true);
13309 result = trans.reader.readRecords(o);
13311 this.fireEvent("loadexception", this, o, trans.arg, e);
13312 trans.callback.call(trans.scope||window, null, trans.arg, false);
13315 this.fireEvent("load", this, o, trans.arg);
13316 trans.callback.call(trans.scope||window, result, trans.arg, true);
13320 handleFailure : function(trans){
13321 this.trans = false;
13322 this.destroyTrans(trans, false);
13323 this.fireEvent("loadexception", this, null, trans.arg);
13324 trans.callback.call(trans.scope||window, null, trans.arg, false);
13328 * Ext JS Library 1.1.1
13329 * Copyright(c) 2006-2007, Ext JS, LLC.
13331 * Originally Released Under LGPL - original licence link has changed is not relivant.
13334 * <script type="text/javascript">
13338 * @class Roo.data.JsonReader
13339 * @extends Roo.data.DataReader
13340 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13341 * based on mappings in a provided Roo.data.Record constructor.
13343 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13344 * in the reply previously.
13349 var RecordDef = Roo.data.Record.create([
13350 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13351 {name: 'occupation'} // This field will use "occupation" as the mapping.
13353 var myReader = new Roo.data.JsonReader({
13354 totalProperty: "results", // The property which contains the total dataset size (optional)
13355 root: "rows", // The property which contains an Array of row objects
13356 id: "id" // The property within each row object that provides an ID for the record (optional)
13360 * This would consume a JSON file like this:
13362 { 'results': 2, 'rows': [
13363 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13364 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13367 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13368 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13369 * paged from the remote server.
13370 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13371 * @cfg {String} root name of the property which contains the Array of row objects.
13372 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13373 * @cfg {Array} fields Array of field definition objects
13375 * Create a new JsonReader
13376 * @param {Object} meta Metadata configuration options
13377 * @param {Object} recordType Either an Array of field definition objects,
13378 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13380 Roo.data.JsonReader = function(meta, recordType){
13383 // set some defaults:
13384 Roo.applyIf(meta, {
13385 totalProperty: 'total',
13386 successProperty : 'success',
13391 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13393 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13395 readerType : 'Json',
13398 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13399 * Used by Store query builder to append _requestMeta to params.
13402 metaFromRemote : false,
13404 * This method is only used by a DataProxy which has retrieved data from a remote server.
13405 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13406 * @return {Object} data A data block which is used by an Roo.data.Store object as
13407 * a cache of Roo.data.Records.
13409 read : function(response){
13410 var json = response.responseText;
13412 var o = /* eval:var:o */ eval("("+json+")");
13414 throw {message: "JsonReader.read: Json object not found"};
13420 this.metaFromRemote = true;
13421 this.meta = o.metaData;
13422 this.recordType = Roo.data.Record.create(o.metaData.fields);
13423 this.onMetaChange(this.meta, this.recordType, o);
13425 return this.readRecords(o);
13428 // private function a store will implement
13429 onMetaChange : function(meta, recordType, o){
13436 simpleAccess: function(obj, subsc) {
13443 getJsonAccessor: function(){
13445 return function(expr) {
13447 return(re.test(expr))
13448 ? new Function("obj", "return obj." + expr)
13453 return Roo.emptyFn;
13458 * Create a data block containing Roo.data.Records from an XML document.
13459 * @param {Object} o An object which contains an Array of row objects in the property specified
13460 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13461 * which contains the total size of the dataset.
13462 * @return {Object} data A data block which is used by an Roo.data.Store object as
13463 * a cache of Roo.data.Records.
13465 readRecords : function(o){
13467 * After any data loads, the raw JSON data is available for further custom processing.
13471 var s = this.meta, Record = this.recordType,
13472 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13474 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13476 if(s.totalProperty) {
13477 this.getTotal = this.getJsonAccessor(s.totalProperty);
13479 if(s.successProperty) {
13480 this.getSuccess = this.getJsonAccessor(s.successProperty);
13482 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13484 var g = this.getJsonAccessor(s.id);
13485 this.getId = function(rec) {
13487 return (r === undefined || r === "") ? null : r;
13490 this.getId = function(){return null;};
13493 for(var jj = 0; jj < fl; jj++){
13495 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13496 this.ef[jj] = this.getJsonAccessor(map);
13500 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13501 if(s.totalProperty){
13502 var vt = parseInt(this.getTotal(o), 10);
13507 if(s.successProperty){
13508 var vs = this.getSuccess(o);
13509 if(vs === false || vs === 'false'){
13514 for(var i = 0; i < c; i++){
13517 var id = this.getId(n);
13518 for(var j = 0; j < fl; j++){
13520 var v = this.ef[j](n);
13522 Roo.log('missing convert for ' + f.name);
13526 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13528 var record = new Record(values, id);
13530 records[i] = record;
13536 totalRecords : totalRecords
13539 // used when loading children.. @see loadDataFromChildren
13540 toLoadData: function(rec)
13542 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13543 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13544 return { data : data, total : data.length };
13549 * Ext JS Library 1.1.1
13550 * Copyright(c) 2006-2007, Ext JS, LLC.
13552 * Originally Released Under LGPL - original licence link has changed is not relivant.
13555 * <script type="text/javascript">
13559 * @class Roo.data.ArrayReader
13560 * @extends Roo.data.DataReader
13561 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13562 * Each element of that Array represents a row of data fields. The
13563 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13564 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13568 var RecordDef = Roo.data.Record.create([
13569 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13570 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13572 var myReader = new Roo.data.ArrayReader({
13573 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13577 * This would consume an Array like this:
13579 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13583 * Create a new JsonReader
13584 * @param {Object} meta Metadata configuration options.
13585 * @param {Object|Array} recordType Either an Array of field definition objects
13587 * @cfg {Array} fields Array of field definition objects
13588 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13589 * as specified to {@link Roo.data.Record#create},
13590 * or an {@link Roo.data.Record} object
13593 * created using {@link Roo.data.Record#create}.
13595 Roo.data.ArrayReader = function(meta, recordType)
13597 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13600 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13603 * Create a data block containing Roo.data.Records from an XML document.
13604 * @param {Object} o An Array of row objects which represents the dataset.
13605 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13606 * a cache of Roo.data.Records.
13608 readRecords : function(o)
13610 var sid = this.meta ? this.meta.id : null;
13611 var recordType = this.recordType, fields = recordType.prototype.fields;
13614 for(var i = 0; i < root.length; i++){
13617 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13618 for(var j = 0, jlen = fields.length; j < jlen; j++){
13619 var f = fields.items[j];
13620 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13621 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13623 values[f.name] = v;
13625 var record = new recordType(values, id);
13627 records[records.length] = record;
13631 totalRecords : records.length
13634 // used when loading children.. @see loadDataFromChildren
13635 toLoadData: function(rec)
13637 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13638 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13649 * @class Roo.bootstrap.ComboBox
13650 * @extends Roo.bootstrap.TriggerField
13651 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13652 * @cfg {Boolean} append (true|false) default false
13653 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13654 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13655 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13656 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13657 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13658 * @cfg {Boolean} animate default true
13659 * @cfg {Boolean} emptyResultText only for touch device
13660 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13661 * @cfg {String} emptyTitle default ''
13663 * Create a new ComboBox.
13664 * @param {Object} config Configuration options
13666 Roo.bootstrap.ComboBox = function(config){
13667 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13671 * Fires when the dropdown list is expanded
13672 * @param {Roo.bootstrap.ComboBox} combo This combo box
13677 * Fires when the dropdown list is collapsed
13678 * @param {Roo.bootstrap.ComboBox} combo This combo box
13682 * @event beforeselect
13683 * Fires before a list item is selected. Return false to cancel the selection.
13684 * @param {Roo.bootstrap.ComboBox} combo This combo box
13685 * @param {Roo.data.Record} record The data record returned from the underlying store
13686 * @param {Number} index The index of the selected item in the dropdown list
13688 'beforeselect' : true,
13691 * Fires when a list item is selected
13692 * @param {Roo.bootstrap.ComboBox} combo This combo box
13693 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13694 * @param {Number} index The index of the selected item in the dropdown list
13698 * @event beforequery
13699 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13700 * The event object passed has these properties:
13701 * @param {Roo.bootstrap.ComboBox} combo This combo box
13702 * @param {String} query The query
13703 * @param {Boolean} forceAll true to force "all" query
13704 * @param {Boolean} cancel true to cancel the query
13705 * @param {Object} e The query event object
13707 'beforequery': true,
13710 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13711 * @param {Roo.bootstrap.ComboBox} combo This combo box
13716 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13717 * @param {Roo.bootstrap.ComboBox} combo This combo box
13718 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13723 * Fires when the remove value from the combobox array
13724 * @param {Roo.bootstrap.ComboBox} combo This combo box
13728 * @event afterremove
13729 * Fires when the remove value from the combobox array
13730 * @param {Roo.bootstrap.ComboBox} combo This combo box
13732 'afterremove' : true,
13734 * @event specialfilter
13735 * Fires when specialfilter
13736 * @param {Roo.bootstrap.ComboBox} combo This combo box
13738 'specialfilter' : true,
13741 * Fires when tick the element
13742 * @param {Roo.bootstrap.ComboBox} combo This combo box
13746 * @event touchviewdisplay
13747 * Fires when touch view require special display (default is using displayField)
13748 * @param {Roo.bootstrap.ComboBox} combo This combo box
13749 * @param {Object} cfg set html .
13751 'touchviewdisplay' : true
13756 this.tickItems = [];
13758 this.selectedIndex = -1;
13759 if(this.mode == 'local'){
13760 if(config.queryDelay === undefined){
13761 this.queryDelay = 10;
13763 if(config.minChars === undefined){
13769 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13772 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13773 * rendering into an Roo.Editor, defaults to false)
13776 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13777 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13780 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13783 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13784 * the dropdown list (defaults to undefined, with no header element)
13788 * @cfg {String/Roo.Template} tpl The template to use to render the output
13792 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13794 listWidth: undefined,
13796 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13797 * mode = 'remote' or 'text' if mode = 'local')
13799 displayField: undefined,
13802 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13803 * mode = 'remote' or 'value' if mode = 'local').
13804 * Note: use of a valueField requires the user make a selection
13805 * in order for a value to be mapped.
13807 valueField: undefined,
13809 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13814 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13815 * field's data value (defaults to the underlying DOM element's name)
13817 hiddenName: undefined,
13819 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13823 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13825 selectedClass: 'active',
13828 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13832 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13833 * anchor positions (defaults to 'tl-bl')
13835 listAlign: 'tl-bl?',
13837 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13841 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13842 * query specified by the allQuery config option (defaults to 'query')
13844 triggerAction: 'query',
13846 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13847 * (defaults to 4, does not apply if editable = false)
13851 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13852 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13856 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13857 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13861 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13862 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13866 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13867 * when editable = true (defaults to false)
13869 selectOnFocus:false,
13871 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13873 queryParam: 'query',
13875 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13876 * when mode = 'remote' (defaults to 'Loading...')
13878 loadingText: 'Loading...',
13880 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13884 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13888 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13889 * traditional select (defaults to true)
13893 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13897 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13901 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13902 * listWidth has a higher value)
13906 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13907 * allow the user to set arbitrary text into the field (defaults to false)
13909 forceSelection:false,
13911 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13912 * if typeAhead = true (defaults to 250)
13914 typeAheadDelay : 250,
13916 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13917 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13919 valueNotFoundText : undefined,
13921 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13923 blockFocus : false,
13926 * @cfg {Boolean} disableClear Disable showing of clear button.
13928 disableClear : false,
13930 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13932 alwaysQuery : false,
13935 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13940 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13942 invalidClass : "has-warning",
13945 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13947 validClass : "has-success",
13950 * @cfg {Boolean} specialFilter (true|false) special filter default false
13952 specialFilter : false,
13955 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13957 mobileTouchView : true,
13960 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13962 useNativeIOS : false,
13965 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13967 mobile_restrict_height : false,
13969 ios_options : false,
13981 btnPosition : 'right',
13982 triggerList : true,
13983 showToggleBtn : true,
13985 emptyResultText: 'Empty',
13986 triggerText : 'Select',
13989 // element that contains real text value.. (when hidden is used..)
13991 getAutoCreate : function()
13996 * Render classic select for iso
13999 if(Roo.isIOS && this.useNativeIOS){
14000 cfg = this.getAutoCreateNativeIOS();
14008 if(Roo.isTouch && this.mobileTouchView){
14009 cfg = this.getAutoCreateTouchView();
14016 if(!this.tickable){
14017 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14022 * ComboBox with tickable selections
14025 var align = this.labelAlign || this.parentLabelAlign();
14028 cls : 'form-group roo-combobox-tickable' //input-group
14031 var btn_text_select = '';
14032 var btn_text_done = '';
14033 var btn_text_cancel = '';
14035 if (this.btn_text_show) {
14036 btn_text_select = 'Select';
14037 btn_text_done = 'Done';
14038 btn_text_cancel = 'Cancel';
14043 cls : 'tickable-buttons',
14048 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14049 //html : this.triggerText
14050 html: btn_text_select
14056 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14058 html: btn_text_done
14064 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14066 html: btn_text_cancel
14072 buttons.cn.unshift({
14074 cls: 'roo-select2-search-field-input'
14080 Roo.each(buttons.cn, function(c){
14082 c.cls += ' btn-' + _this.size;
14085 if (_this.disabled) {
14092 style : 'display: contents',
14097 cls: 'form-hidden-field'
14101 cls: 'roo-select2-choices',
14105 cls: 'roo-select2-search-field',
14116 cls: 'roo-select2-container input-group roo-select2-container-multi',
14122 // cls: 'typeahead typeahead-long dropdown-menu',
14123 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
14128 if(this.hasFeedback && !this.allowBlank){
14132 cls: 'glyphicon form-control-feedback'
14135 combobox.cn.push(feedback);
14140 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14141 tooltip : 'This field is required'
14143 if (Roo.bootstrap.version == 4) {
14146 style : 'display:none'
14149 if (align ==='left' && this.fieldLabel.length) {
14151 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14158 cls : 'control-label col-form-label',
14159 html : this.fieldLabel
14171 var labelCfg = cfg.cn[1];
14172 var contentCfg = cfg.cn[2];
14175 if(this.indicatorpos == 'right'){
14181 cls : 'control-label col-form-label',
14185 html : this.fieldLabel
14201 labelCfg = cfg.cn[0];
14202 contentCfg = cfg.cn[1];
14206 if(this.labelWidth > 12){
14207 labelCfg.style = "width: " + this.labelWidth + 'px';
14210 if(this.labelWidth < 13 && this.labelmd == 0){
14211 this.labelmd = this.labelWidth;
14214 if(this.labellg > 0){
14215 labelCfg.cls += ' col-lg-' + this.labellg;
14216 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14219 if(this.labelmd > 0){
14220 labelCfg.cls += ' col-md-' + this.labelmd;
14221 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14224 if(this.labelsm > 0){
14225 labelCfg.cls += ' col-sm-' + this.labelsm;
14226 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14229 if(this.labelxs > 0){
14230 labelCfg.cls += ' col-xs-' + this.labelxs;
14231 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14235 } else if ( this.fieldLabel.length) {
14236 // Roo.log(" label");
14241 //cls : 'input-group-addon',
14242 html : this.fieldLabel
14247 if(this.indicatorpos == 'right'){
14251 //cls : 'input-group-addon',
14252 html : this.fieldLabel
14262 // Roo.log(" no label && no align");
14269 ['xs','sm','md','lg'].map(function(size){
14270 if (settings[size]) {
14271 cfg.cls += ' col-' + size + '-' + settings[size];
14279 _initEventsCalled : false,
14282 initEvents: function()
14284 if (this._initEventsCalled) { // as we call render... prevent looping...
14287 this._initEventsCalled = true;
14290 throw "can not find store for combo";
14293 this.indicator = this.indicatorEl();
14295 this.store = Roo.factory(this.store, Roo.data);
14296 this.store.parent = this;
14298 // if we are building from html. then this element is so complex, that we can not really
14299 // use the rendered HTML.
14300 // so we have to trash and replace the previous code.
14301 if (Roo.XComponent.build_from_html) {
14302 // remove this element....
14303 var e = this.el.dom, k=0;
14304 while (e ) { e = e.previousSibling; ++k;}
14309 this.rendered = false;
14311 this.render(this.parent().getChildContainer(true), k);
14314 if(Roo.isIOS && this.useNativeIOS){
14315 this.initIOSView();
14323 if(Roo.isTouch && this.mobileTouchView){
14324 this.initTouchView();
14329 this.initTickableEvents();
14333 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14335 if(this.hiddenName){
14337 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14339 this.hiddenField.dom.value =
14340 this.hiddenValue !== undefined ? this.hiddenValue :
14341 this.value !== undefined ? this.value : '';
14343 // prevent input submission
14344 this.el.dom.removeAttribute('name');
14345 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14350 // this.el.dom.setAttribute('autocomplete', 'off');
14353 var cls = 'x-combo-list';
14355 //this.list = new Roo.Layer({
14356 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14362 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14363 _this.list.setWidth(lw);
14366 this.list.on('mouseover', this.onViewOver, this);
14367 this.list.on('mousemove', this.onViewMove, this);
14368 this.list.on('scroll', this.onViewScroll, this);
14371 this.list.swallowEvent('mousewheel');
14372 this.assetHeight = 0;
14375 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14376 this.assetHeight += this.header.getHeight();
14379 this.innerList = this.list.createChild({cls:cls+'-inner'});
14380 this.innerList.on('mouseover', this.onViewOver, this);
14381 this.innerList.on('mousemove', this.onViewMove, this);
14382 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14384 if(this.allowBlank && !this.pageSize && !this.disableClear){
14385 this.footer = this.list.createChild({cls:cls+'-ft'});
14386 this.pageTb = new Roo.Toolbar(this.footer);
14390 this.footer = this.list.createChild({cls:cls+'-ft'});
14391 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14392 {pageSize: this.pageSize});
14396 if (this.pageTb && this.allowBlank && !this.disableClear) {
14398 this.pageTb.add(new Roo.Toolbar.Fill(), {
14399 cls: 'x-btn-icon x-btn-clear',
14401 handler: function()
14404 _this.clearValue();
14405 _this.onSelect(false, -1);
14410 this.assetHeight += this.footer.getHeight();
14415 this.tpl = Roo.bootstrap.version == 4 ?
14416 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14417 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14420 this.view = new Roo.View(this.list, this.tpl, {
14421 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14423 //this.view.wrapEl.setDisplayed(false);
14424 this.view.on('click', this.onViewClick, this);
14427 this.store.on('beforeload', this.onBeforeLoad, this);
14428 this.store.on('load', this.onLoad, this);
14429 this.store.on('loadexception', this.onLoadException, this);
14431 if(this.resizable){
14432 this.resizer = new Roo.Resizable(this.list, {
14433 pinned:true, handles:'se'
14435 this.resizer.on('resize', function(r, w, h){
14436 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14437 this.listWidth = w;
14438 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14439 this.restrictHeight();
14441 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14444 if(!this.editable){
14445 this.editable = true;
14446 this.setEditable(false);
14451 if (typeof(this.events.add.listeners) != 'undefined') {
14453 this.addicon = this.wrap.createChild(
14454 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14456 this.addicon.on('click', function(e) {
14457 this.fireEvent('add', this);
14460 if (typeof(this.events.edit.listeners) != 'undefined') {
14462 this.editicon = this.wrap.createChild(
14463 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14464 if (this.addicon) {
14465 this.editicon.setStyle('margin-left', '40px');
14467 this.editicon.on('click', function(e) {
14469 // we fire even if inothing is selected..
14470 this.fireEvent('edit', this, this.lastData );
14476 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14477 "up" : function(e){
14478 this.inKeyMode = true;
14482 "down" : function(e){
14483 if(!this.isExpanded()){
14484 this.onTriggerClick();
14486 this.inKeyMode = true;
14491 "enter" : function(e){
14492 // this.onViewClick();
14496 if(this.fireEvent("specialkey", this, e)){
14497 this.onViewClick(false);
14503 "esc" : function(e){
14507 "tab" : function(e){
14510 if(this.fireEvent("specialkey", this, e)){
14511 this.onViewClick(false);
14519 doRelay : function(foo, bar, hname){
14520 if(hname == 'down' || this.scope.isExpanded()){
14521 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14530 this.queryDelay = Math.max(this.queryDelay || 10,
14531 this.mode == 'local' ? 10 : 250);
14534 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14536 if(this.typeAhead){
14537 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14539 if(this.editable !== false){
14540 this.inputEl().on("keyup", this.onKeyUp, this);
14542 if(this.forceSelection){
14543 this.inputEl().on('blur', this.doForce, this);
14547 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14548 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14552 initTickableEvents: function()
14556 if(this.hiddenName){
14558 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14560 this.hiddenField.dom.value =
14561 this.hiddenValue !== undefined ? this.hiddenValue :
14562 this.value !== undefined ? this.value : '';
14564 // prevent input submission
14565 this.el.dom.removeAttribute('name');
14566 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14571 // this.list = this.el.select('ul.dropdown-menu',true).first();
14573 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14574 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14575 if(this.triggerList){
14576 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14579 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14580 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14582 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14583 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14585 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14586 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14588 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14589 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14590 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14593 this.cancelBtn.hide();
14598 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14599 _this.list.setWidth(lw);
14602 this.list.on('mouseover', this.onViewOver, this);
14603 this.list.on('mousemove', this.onViewMove, this);
14605 this.list.on('scroll', this.onViewScroll, this);
14608 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14609 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14612 this.view = new Roo.View(this.list, this.tpl, {
14617 selectedClass: this.selectedClass
14620 //this.view.wrapEl.setDisplayed(false);
14621 this.view.on('click', this.onViewClick, this);
14625 this.store.on('beforeload', this.onBeforeLoad, this);
14626 this.store.on('load', this.onLoad, this);
14627 this.store.on('loadexception', this.onLoadException, this);
14630 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14631 "up" : function(e){
14632 this.inKeyMode = true;
14636 "down" : function(e){
14637 this.inKeyMode = true;
14641 "enter" : function(e){
14642 if(this.fireEvent("specialkey", this, e)){
14643 this.onViewClick(false);
14649 "esc" : function(e){
14650 this.onTickableFooterButtonClick(e, false, false);
14653 "tab" : function(e){
14654 this.fireEvent("specialkey", this, e);
14656 this.onTickableFooterButtonClick(e, false, false);
14663 doRelay : function(e, fn, key){
14664 if(this.scope.isExpanded()){
14665 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14674 this.queryDelay = Math.max(this.queryDelay || 10,
14675 this.mode == 'local' ? 10 : 250);
14678 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14680 if(this.typeAhead){
14681 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14684 if(this.editable !== false){
14685 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14688 this.indicator = this.indicatorEl();
14690 if(this.indicator){
14691 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14692 this.indicator.hide();
14697 onDestroy : function(){
14699 this.view.setStore(null);
14700 this.view.el.removeAllListeners();
14701 this.view.el.remove();
14702 this.view.purgeListeners();
14705 this.list.dom.innerHTML = '';
14709 this.store.un('beforeload', this.onBeforeLoad, this);
14710 this.store.un('load', this.onLoad, this);
14711 this.store.un('loadexception', this.onLoadException, this);
14713 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14717 fireKey : function(e){
14718 if(e.isNavKeyPress() && !this.list.isVisible()){
14719 this.fireEvent("specialkey", this, e);
14724 onResize: function(w, h){
14725 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14727 // if(typeof w != 'number'){
14728 // // we do not handle it!?!?
14731 // var tw = this.trigger.getWidth();
14732 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14733 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14735 // this.inputEl().setWidth( this.adjustWidth('input', x));
14737 // //this.trigger.setStyle('left', x+'px');
14739 // if(this.list && this.listWidth === undefined){
14740 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14741 // this.list.setWidth(lw);
14742 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14750 * Allow or prevent the user from directly editing the field text. If false is passed,
14751 * the user will only be able to select from the items defined in the dropdown list. This method
14752 * is the runtime equivalent of setting the 'editable' config option at config time.
14753 * @param {Boolean} value True to allow the user to directly edit the field text
14755 setEditable : function(value){
14756 if(value == this.editable){
14759 this.editable = value;
14761 this.inputEl().dom.setAttribute('readOnly', true);
14762 this.inputEl().on('mousedown', this.onTriggerClick, this);
14763 this.inputEl().addClass('x-combo-noedit');
14765 this.inputEl().dom.setAttribute('readOnly', false);
14766 this.inputEl().un('mousedown', this.onTriggerClick, this);
14767 this.inputEl().removeClass('x-combo-noedit');
14773 onBeforeLoad : function(combo,opts){
14774 if(!this.hasFocus){
14778 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14780 this.restrictHeight();
14781 this.selectedIndex = -1;
14785 onLoad : function(){
14787 this.hasQuery = false;
14789 if(!this.hasFocus){
14793 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14794 this.loading.hide();
14797 if(this.store.getCount() > 0){
14800 this.restrictHeight();
14801 if(this.lastQuery == this.allQuery){
14802 if(this.editable && !this.tickable){
14803 this.inputEl().dom.select();
14807 !this.selectByValue(this.value, true) &&
14810 !this.store.lastOptions ||
14811 typeof(this.store.lastOptions.add) == 'undefined' ||
14812 this.store.lastOptions.add != true
14815 this.select(0, true);
14818 if(this.autoFocus){
14821 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14822 this.taTask.delay(this.typeAheadDelay);
14826 this.onEmptyResults();
14832 onLoadException : function()
14834 this.hasQuery = false;
14836 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14837 this.loading.hide();
14840 if(this.tickable && this.editable){
14845 // only causes errors at present
14846 //Roo.log(this.store.reader.jsonData);
14847 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14849 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14855 onTypeAhead : function(){
14856 if(this.store.getCount() > 0){
14857 var r = this.store.getAt(0);
14858 var newValue = r.data[this.displayField];
14859 var len = newValue.length;
14860 var selStart = this.getRawValue().length;
14862 if(selStart != len){
14863 this.setRawValue(newValue);
14864 this.selectText(selStart, newValue.length);
14870 onSelect : function(record, index){
14872 if(this.fireEvent('beforeselect', this, record, index) !== false){
14874 this.setFromData(index > -1 ? record.data : false);
14877 this.fireEvent('select', this, record, index);
14882 * Returns the currently selected field value or empty string if no value is set.
14883 * @return {String} value The selected value
14885 getValue : function()
14887 if(Roo.isIOS && this.useNativeIOS){
14888 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14892 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14895 if(this.valueField){
14896 return typeof this.value != 'undefined' ? this.value : '';
14898 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14902 getRawValue : function()
14904 if(Roo.isIOS && this.useNativeIOS){
14905 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14908 var v = this.inputEl().getValue();
14914 * Clears any text/value currently set in the field
14916 clearValue : function(){
14918 if(this.hiddenField){
14919 this.hiddenField.dom.value = '';
14922 this.setRawValue('');
14923 this.lastSelectionText = '';
14924 this.lastData = false;
14926 var close = this.closeTriggerEl();
14937 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14938 * will be displayed in the field. If the value does not match the data value of an existing item,
14939 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14940 * Otherwise the field will be blank (although the value will still be set).
14941 * @param {String} value The value to match
14943 setValue : function(v)
14945 if(Roo.isIOS && this.useNativeIOS){
14946 this.setIOSValue(v);
14956 if(this.valueField){
14957 var r = this.findRecord(this.valueField, v);
14959 text = r.data[this.displayField];
14960 }else if(this.valueNotFoundText !== undefined){
14961 text = this.valueNotFoundText;
14964 this.lastSelectionText = text;
14965 if(this.hiddenField){
14966 this.hiddenField.dom.value = v;
14968 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14971 var close = this.closeTriggerEl();
14974 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14980 * @property {Object} the last set data for the element
14985 * Sets the value of the field based on a object which is related to the record format for the store.
14986 * @param {Object} value the value to set as. or false on reset?
14988 setFromData : function(o){
14995 var dv = ''; // display value
14996 var vv = ''; // value value..
14998 if (this.displayField) {
14999 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15001 // this is an error condition!!!
15002 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15005 if(this.valueField){
15006 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15009 var close = this.closeTriggerEl();
15012 if(dv.length || vv * 1 > 0){
15014 this.blockFocus=true;
15020 if(this.hiddenField){
15021 this.hiddenField.dom.value = vv;
15023 this.lastSelectionText = dv;
15024 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15028 // no hidden field.. - we store the value in 'value', but still display
15029 // display field!!!!
15030 this.lastSelectionText = dv;
15031 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15038 reset : function(){
15039 // overridden so that last data is reset..
15046 this.setValue(this.originalValue);
15047 //this.clearInvalid();
15048 this.lastData = false;
15050 this.view.clearSelections();
15056 findRecord : function(prop, value){
15058 if(this.store.getCount() > 0){
15059 this.store.each(function(r){
15060 if(r.data[prop] == value){
15070 getName: function()
15072 // returns hidden if it's set..
15073 if (!this.rendered) {return ''};
15074 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
15078 onViewMove : function(e, t){
15079 this.inKeyMode = false;
15083 onViewOver : function(e, t){
15084 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15087 var item = this.view.findItemFromChild(t);
15090 var index = this.view.indexOf(item);
15091 this.select(index, false);
15096 onViewClick : function(view, doFocus, el, e)
15098 var index = this.view.getSelectedIndexes()[0];
15100 var r = this.store.getAt(index);
15104 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15111 Roo.each(this.tickItems, function(v,k){
15113 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15115 _this.tickItems.splice(k, 1);
15117 if(typeof(e) == 'undefined' && view == false){
15118 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15130 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15131 this.tickItems.push(r.data);
15134 if(typeof(e) == 'undefined' && view == false){
15135 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15142 this.onSelect(r, index);
15144 if(doFocus !== false && !this.blockFocus){
15145 this.inputEl().focus();
15150 restrictHeight : function(){
15151 //this.innerList.dom.style.height = '';
15152 //var inner = this.innerList.dom;
15153 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15154 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15155 //this.list.beginUpdate();
15156 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15157 this.list.alignTo(this.inputEl(), this.listAlign);
15158 this.list.alignTo(this.inputEl(), this.listAlign);
15159 //this.list.endUpdate();
15163 onEmptyResults : function(){
15165 if(this.tickable && this.editable){
15166 this.hasFocus = false;
15167 this.restrictHeight();
15175 * Returns true if the dropdown list is expanded, else false.
15177 isExpanded : function(){
15178 return this.list.isVisible();
15182 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15183 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15184 * @param {String} value The data value of the item to select
15185 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15186 * selected item if it is not currently in view (defaults to true)
15187 * @return {Boolean} True if the value matched an item in the list, else false
15189 selectByValue : function(v, scrollIntoView){
15190 if(v !== undefined && v !== null){
15191 var r = this.findRecord(this.valueField || this.displayField, v);
15193 this.select(this.store.indexOf(r), scrollIntoView);
15201 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15202 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15203 * @param {Number} index The zero-based index of the list item to select
15204 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15205 * selected item if it is not currently in view (defaults to true)
15207 select : function(index, scrollIntoView){
15208 this.selectedIndex = index;
15209 this.view.select(index);
15210 if(scrollIntoView !== false){
15211 var el = this.view.getNode(index);
15213 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15216 this.list.scrollChildIntoView(el, false);
15222 selectNext : function(){
15223 var ct = this.store.getCount();
15225 if(this.selectedIndex == -1){
15227 }else if(this.selectedIndex < ct-1){
15228 this.select(this.selectedIndex+1);
15234 selectPrev : function(){
15235 var ct = this.store.getCount();
15237 if(this.selectedIndex == -1){
15239 }else if(this.selectedIndex != 0){
15240 this.select(this.selectedIndex-1);
15246 onKeyUp : function(e){
15247 if(this.editable !== false && !e.isSpecialKey()){
15248 this.lastKey = e.getKey();
15249 this.dqTask.delay(this.queryDelay);
15254 validateBlur : function(){
15255 return !this.list || !this.list.isVisible();
15259 initQuery : function(){
15261 var v = this.getRawValue();
15263 if(this.tickable && this.editable){
15264 v = this.tickableInputEl().getValue();
15271 doForce : function(){
15272 if(this.inputEl().dom.value.length > 0){
15273 this.inputEl().dom.value =
15274 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15280 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15281 * query allowing the query action to be canceled if needed.
15282 * @param {String} query The SQL query to execute
15283 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15284 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15285 * saved in the current store (defaults to false)
15287 doQuery : function(q, forceAll){
15289 if(q === undefined || q === null){
15294 forceAll: forceAll,
15298 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15303 forceAll = qe.forceAll;
15304 if(forceAll === true || (q.length >= this.minChars)){
15306 this.hasQuery = true;
15308 if(this.lastQuery != q || this.alwaysQuery){
15309 this.lastQuery = q;
15310 if(this.mode == 'local'){
15311 this.selectedIndex = -1;
15313 this.store.clearFilter();
15316 if(this.specialFilter){
15317 this.fireEvent('specialfilter', this);
15322 this.store.filter(this.displayField, q);
15325 this.store.fireEvent("datachanged", this.store);
15332 this.store.baseParams[this.queryParam] = q;
15334 var options = {params : this.getParams(q)};
15337 options.add = true;
15338 options.params.start = this.page * this.pageSize;
15341 this.store.load(options);
15344 * this code will make the page width larger, at the beginning, the list not align correctly,
15345 * we should expand the list on onLoad
15346 * so command out it
15351 this.selectedIndex = -1;
15356 this.loadNext = false;
15360 getParams : function(q){
15362 //p[this.queryParam] = q;
15366 p.limit = this.pageSize;
15372 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15374 collapse : function(){
15375 if(!this.isExpanded()){
15381 this.hasFocus = false;
15385 this.cancelBtn.hide();
15386 this.trigger.show();
15389 this.tickableInputEl().dom.value = '';
15390 this.tickableInputEl().blur();
15395 Roo.get(document).un('mousedown', this.collapseIf, this);
15396 Roo.get(document).un('mousewheel', this.collapseIf, this);
15397 if (!this.editable) {
15398 Roo.get(document).un('keydown', this.listKeyPress, this);
15400 this.fireEvent('collapse', this);
15406 collapseIf : function(e){
15407 var in_combo = e.within(this.el);
15408 var in_list = e.within(this.list);
15409 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15411 if (in_combo || in_list || is_list) {
15412 //e.stopPropagation();
15417 this.onTickableFooterButtonClick(e, false, false);
15425 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15427 expand : function(){
15429 if(this.isExpanded() || !this.hasFocus){
15433 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15434 this.list.setWidth(lw);
15440 this.restrictHeight();
15444 this.tickItems = Roo.apply([], this.item);
15447 this.cancelBtn.show();
15448 this.trigger.hide();
15451 this.tickableInputEl().focus();
15456 Roo.get(document).on('mousedown', this.collapseIf, this);
15457 Roo.get(document).on('mousewheel', this.collapseIf, this);
15458 if (!this.editable) {
15459 Roo.get(document).on('keydown', this.listKeyPress, this);
15462 this.fireEvent('expand', this);
15466 // Implements the default empty TriggerField.onTriggerClick function
15467 onTriggerClick : function(e)
15469 Roo.log('trigger click');
15471 if(this.disabled || !this.triggerList){
15476 this.loadNext = false;
15478 if(this.isExpanded()){
15480 if (!this.blockFocus) {
15481 this.inputEl().focus();
15485 this.hasFocus = true;
15486 if(this.triggerAction == 'all') {
15487 this.doQuery(this.allQuery, true);
15489 this.doQuery(this.getRawValue());
15491 if (!this.blockFocus) {
15492 this.inputEl().focus();
15497 onTickableTriggerClick : function(e)
15504 this.loadNext = false;
15505 this.hasFocus = true;
15507 if(this.triggerAction == 'all') {
15508 this.doQuery(this.allQuery, true);
15510 this.doQuery(this.getRawValue());
15514 onSearchFieldClick : function(e)
15516 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15517 this.onTickableFooterButtonClick(e, false, false);
15521 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15526 this.loadNext = false;
15527 this.hasFocus = true;
15529 if(this.triggerAction == 'all') {
15530 this.doQuery(this.allQuery, true);
15532 this.doQuery(this.getRawValue());
15536 listKeyPress : function(e)
15538 //Roo.log('listkeypress');
15539 // scroll to first matching element based on key pres..
15540 if (e.isSpecialKey()) {
15543 var k = String.fromCharCode(e.getKey()).toUpperCase();
15546 var csel = this.view.getSelectedNodes();
15547 var cselitem = false;
15549 var ix = this.view.indexOf(csel[0]);
15550 cselitem = this.store.getAt(ix);
15551 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15557 this.store.each(function(v) {
15559 // start at existing selection.
15560 if (cselitem.id == v.id) {
15566 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15567 match = this.store.indexOf(v);
15573 if (match === false) {
15574 return true; // no more action?
15577 this.view.select(match);
15578 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15579 sn.scrollIntoView(sn.dom.parentNode, false);
15582 onViewScroll : function(e, t){
15584 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){
15588 this.hasQuery = true;
15590 this.loading = this.list.select('.loading', true).first();
15592 if(this.loading === null){
15593 this.list.createChild({
15595 cls: 'loading roo-select2-more-results roo-select2-active',
15596 html: 'Loading more results...'
15599 this.loading = this.list.select('.loading', true).first();
15601 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15603 this.loading.hide();
15606 this.loading.show();
15611 this.loadNext = true;
15613 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15618 addItem : function(o)
15620 var dv = ''; // display value
15622 if (this.displayField) {
15623 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15625 // this is an error condition!!!
15626 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15633 var choice = this.choices.createChild({
15635 cls: 'roo-select2-search-choice',
15644 cls: 'roo-select2-search-choice-close fa fa-times',
15649 }, this.searchField);
15651 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15653 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15661 this.inputEl().dom.value = '';
15666 onRemoveItem : function(e, _self, o)
15668 e.preventDefault();
15670 this.lastItem = Roo.apply([], this.item);
15672 var index = this.item.indexOf(o.data) * 1;
15675 Roo.log('not this item?!');
15679 this.item.splice(index, 1);
15684 this.fireEvent('remove', this, e);
15690 syncValue : function()
15692 if(!this.item.length){
15699 Roo.each(this.item, function(i){
15700 if(_this.valueField){
15701 value.push(i[_this.valueField]);
15708 this.value = value.join(',');
15710 if(this.hiddenField){
15711 this.hiddenField.dom.value = this.value;
15714 this.store.fireEvent("datachanged", this.store);
15719 clearItem : function()
15721 if(!this.multiple){
15727 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15735 if(this.tickable && !Roo.isTouch){
15736 this.view.refresh();
15740 inputEl: function ()
15742 if(Roo.isIOS && this.useNativeIOS){
15743 return this.el.select('select.roo-ios-select', true).first();
15746 if(Roo.isTouch && this.mobileTouchView){
15747 return this.el.select('input.form-control',true).first();
15751 return this.searchField;
15754 return this.el.select('input.form-control',true).first();
15757 onTickableFooterButtonClick : function(e, btn, el)
15759 e.preventDefault();
15761 this.lastItem = Roo.apply([], this.item);
15763 if(btn && btn.name == 'cancel'){
15764 this.tickItems = Roo.apply([], this.item);
15773 Roo.each(this.tickItems, function(o){
15781 validate : function()
15783 if(this.getVisibilityEl().hasClass('hidden')){
15787 var v = this.getRawValue();
15790 v = this.getValue();
15793 if(this.disabled || this.allowBlank || v.length){
15798 this.markInvalid();
15802 tickableInputEl : function()
15804 if(!this.tickable || !this.editable){
15805 return this.inputEl();
15808 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15812 getAutoCreateTouchView : function()
15817 cls: 'form-group' //input-group
15823 type : this.inputType,
15824 cls : 'form-control x-combo-noedit',
15825 autocomplete: 'new-password',
15826 placeholder : this.placeholder || '',
15831 input.name = this.name;
15835 input.cls += ' input-' + this.size;
15838 if (this.disabled) {
15839 input.disabled = true;
15850 inputblock.cls += ' input-group';
15852 inputblock.cn.unshift({
15854 cls : 'input-group-addon input-group-prepend input-group-text',
15859 if(this.removable && !this.multiple){
15860 inputblock.cls += ' roo-removable';
15862 inputblock.cn.push({
15865 cls : 'roo-combo-removable-btn close'
15869 if(this.hasFeedback && !this.allowBlank){
15871 inputblock.cls += ' has-feedback';
15873 inputblock.cn.push({
15875 cls: 'glyphicon form-control-feedback'
15882 inputblock.cls += (this.before) ? '' : ' input-group';
15884 inputblock.cn.push({
15886 cls : 'input-group-addon input-group-append input-group-text',
15892 var ibwrap = inputblock;
15897 cls: 'roo-select2-choices',
15901 cls: 'roo-select2-search-field',
15914 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15919 cls: 'form-hidden-field'
15925 if(!this.multiple && this.showToggleBtn){
15931 if (this.caret != false) {
15934 cls: 'fa fa-' + this.caret
15941 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15943 Roo.bootstrap.version == 3 ? caret : '',
15946 cls: 'combobox-clear',
15960 combobox.cls += ' roo-select2-container-multi';
15963 var align = this.labelAlign || this.parentLabelAlign();
15965 if (align ==='left' && this.fieldLabel.length) {
15970 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15971 tooltip : 'This field is required'
15975 cls : 'control-label col-form-label',
15976 html : this.fieldLabel
15987 var labelCfg = cfg.cn[1];
15988 var contentCfg = cfg.cn[2];
15991 if(this.indicatorpos == 'right'){
15996 cls : 'control-label col-form-label',
16000 html : this.fieldLabel
16004 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16005 tooltip : 'This field is required'
16018 labelCfg = cfg.cn[0];
16019 contentCfg = cfg.cn[1];
16024 if(this.labelWidth > 12){
16025 labelCfg.style = "width: " + this.labelWidth + 'px';
16028 if(this.labelWidth < 13 && this.labelmd == 0){
16029 this.labelmd = this.labelWidth;
16032 if(this.labellg > 0){
16033 labelCfg.cls += ' col-lg-' + this.labellg;
16034 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16037 if(this.labelmd > 0){
16038 labelCfg.cls += ' col-md-' + this.labelmd;
16039 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16042 if(this.labelsm > 0){
16043 labelCfg.cls += ' col-sm-' + this.labelsm;
16044 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16047 if(this.labelxs > 0){
16048 labelCfg.cls += ' col-xs-' + this.labelxs;
16049 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16053 } else if ( this.fieldLabel.length) {
16057 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16058 tooltip : 'This field is required'
16062 cls : 'control-label',
16063 html : this.fieldLabel
16074 if(this.indicatorpos == 'right'){
16078 cls : 'control-label',
16079 html : this.fieldLabel,
16083 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16084 tooltip : 'This field is required'
16101 var settings = this;
16103 ['xs','sm','md','lg'].map(function(size){
16104 if (settings[size]) {
16105 cfg.cls += ' col-' + size + '-' + settings[size];
16112 initTouchView : function()
16114 this.renderTouchView();
16116 this.touchViewEl.on('scroll', function(){
16117 this.el.dom.scrollTop = 0;
16120 this.originalValue = this.getValue();
16122 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16124 this.inputEl().on("click", this.showTouchView, this);
16125 if (this.triggerEl) {
16126 this.triggerEl.on("click", this.showTouchView, this);
16130 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16131 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16133 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16135 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16136 this.store.on('load', this.onTouchViewLoad, this);
16137 this.store.on('loadexception', this.onTouchViewLoadException, this);
16139 if(this.hiddenName){
16141 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16143 this.hiddenField.dom.value =
16144 this.hiddenValue !== undefined ? this.hiddenValue :
16145 this.value !== undefined ? this.value : '';
16147 this.el.dom.removeAttribute('name');
16148 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16152 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16153 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16156 if(this.removable && !this.multiple){
16157 var close = this.closeTriggerEl();
16159 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16160 close.on('click', this.removeBtnClick, this, close);
16164 * fix the bug in Safari iOS8
16166 this.inputEl().on("focus", function(e){
16167 document.activeElement.blur();
16170 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16177 renderTouchView : function()
16179 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16180 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16182 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16183 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16185 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16186 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16187 this.touchViewBodyEl.setStyle('overflow', 'auto');
16189 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16190 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16192 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16193 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16197 showTouchView : function()
16203 this.touchViewHeaderEl.hide();
16205 if(this.modalTitle.length){
16206 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16207 this.touchViewHeaderEl.show();
16210 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16211 this.touchViewEl.show();
16213 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16215 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16216 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16218 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16220 if(this.modalTitle.length){
16221 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16224 this.touchViewBodyEl.setHeight(bodyHeight);
16228 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16230 this.touchViewEl.addClass('in');
16233 if(this._touchViewMask){
16234 Roo.get(document.body).addClass("x-body-masked");
16235 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16236 this._touchViewMask.setStyle('z-index', 10000);
16237 this._touchViewMask.addClass('show');
16240 this.doTouchViewQuery();
16244 hideTouchView : function()
16246 this.touchViewEl.removeClass('in');
16250 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16252 this.touchViewEl.setStyle('display', 'none');
16255 if(this._touchViewMask){
16256 this._touchViewMask.removeClass('show');
16257 Roo.get(document.body).removeClass("x-body-masked");
16261 setTouchViewValue : function()
16268 Roo.each(this.tickItems, function(o){
16273 this.hideTouchView();
16276 doTouchViewQuery : function()
16285 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16289 if(!this.alwaysQuery || this.mode == 'local'){
16290 this.onTouchViewLoad();
16297 onTouchViewBeforeLoad : function(combo,opts)
16303 onTouchViewLoad : function()
16305 if(this.store.getCount() < 1){
16306 this.onTouchViewEmptyResults();
16310 this.clearTouchView();
16312 var rawValue = this.getRawValue();
16314 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16316 this.tickItems = [];
16318 this.store.data.each(function(d, rowIndex){
16319 var row = this.touchViewListGroup.createChild(template);
16321 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16322 row.addClass(d.data.cls);
16325 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16328 html : d.data[this.displayField]
16331 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16332 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16335 row.removeClass('selected');
16336 if(!this.multiple && this.valueField &&
16337 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16340 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16341 row.addClass('selected');
16344 if(this.multiple && this.valueField &&
16345 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16349 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16350 this.tickItems.push(d.data);
16353 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16357 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16359 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16361 if(this.modalTitle.length){
16362 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16365 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16367 if(this.mobile_restrict_height && listHeight < bodyHeight){
16368 this.touchViewBodyEl.setHeight(listHeight);
16373 if(firstChecked && listHeight > bodyHeight){
16374 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16379 onTouchViewLoadException : function()
16381 this.hideTouchView();
16384 onTouchViewEmptyResults : function()
16386 this.clearTouchView();
16388 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16390 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16394 clearTouchView : function()
16396 this.touchViewListGroup.dom.innerHTML = '';
16399 onTouchViewClick : function(e, el, o)
16401 e.preventDefault();
16404 var rowIndex = o.rowIndex;
16406 var r = this.store.getAt(rowIndex);
16408 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16410 if(!this.multiple){
16411 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16412 c.dom.removeAttribute('checked');
16415 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16417 this.setFromData(r.data);
16419 var close = this.closeTriggerEl();
16425 this.hideTouchView();
16427 this.fireEvent('select', this, r, rowIndex);
16432 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16433 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16434 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16438 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16439 this.addItem(r.data);
16440 this.tickItems.push(r.data);
16444 getAutoCreateNativeIOS : function()
16447 cls: 'form-group' //input-group,
16452 cls : 'roo-ios-select'
16456 combobox.name = this.name;
16459 if (this.disabled) {
16460 combobox.disabled = true;
16463 var settings = this;
16465 ['xs','sm','md','lg'].map(function(size){
16466 if (settings[size]) {
16467 cfg.cls += ' col-' + size + '-' + settings[size];
16477 initIOSView : function()
16479 this.store.on('load', this.onIOSViewLoad, this);
16484 onIOSViewLoad : function()
16486 if(this.store.getCount() < 1){
16490 this.clearIOSView();
16492 if(this.allowBlank) {
16494 var default_text = '-- SELECT --';
16496 if(this.placeholder.length){
16497 default_text = this.placeholder;
16500 if(this.emptyTitle.length){
16501 default_text += ' - ' + this.emptyTitle + ' -';
16504 var opt = this.inputEl().createChild({
16507 html : default_text
16511 o[this.valueField] = 0;
16512 o[this.displayField] = default_text;
16514 this.ios_options.push({
16521 this.store.data.each(function(d, rowIndex){
16525 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16526 html = d.data[this.displayField];
16531 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16532 value = d.data[this.valueField];
16541 if(this.value == d.data[this.valueField]){
16542 option['selected'] = true;
16545 var opt = this.inputEl().createChild(option);
16547 this.ios_options.push({
16554 this.inputEl().on('change', function(){
16555 this.fireEvent('select', this);
16560 clearIOSView: function()
16562 this.inputEl().dom.innerHTML = '';
16564 this.ios_options = [];
16567 setIOSValue: function(v)
16571 if(!this.ios_options){
16575 Roo.each(this.ios_options, function(opts){
16577 opts.el.dom.removeAttribute('selected');
16579 if(opts.data[this.valueField] != v){
16583 opts.el.dom.setAttribute('selected', true);
16589 * @cfg {Boolean} grow
16593 * @cfg {Number} growMin
16597 * @cfg {Number} growMax
16606 Roo.apply(Roo.bootstrap.ComboBox, {
16610 cls: 'modal-header',
16632 cls: 'list-group-item',
16636 cls: 'roo-combobox-list-group-item-value'
16640 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16654 listItemCheckbox : {
16656 cls: 'list-group-item',
16660 cls: 'roo-combobox-list-group-item-value'
16664 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16680 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16685 cls: 'modal-footer',
16693 cls: 'col-xs-6 text-left',
16696 cls: 'btn btn-danger roo-touch-view-cancel',
16702 cls: 'col-xs-6 text-right',
16705 cls: 'btn btn-success roo-touch-view-ok',
16716 Roo.apply(Roo.bootstrap.ComboBox, {
16718 touchViewTemplate : {
16720 cls: 'modal fade roo-combobox-touch-view',
16724 cls: 'modal-dialog',
16725 style : 'position:fixed', // we have to fix position....
16729 cls: 'modal-content',
16731 Roo.bootstrap.ComboBox.header,
16732 Roo.bootstrap.ComboBox.body,
16733 Roo.bootstrap.ComboBox.footer
16742 * Ext JS Library 1.1.1
16743 * Copyright(c) 2006-2007, Ext JS, LLC.
16745 * Originally Released Under LGPL - original licence link has changed is not relivant.
16748 * <script type="text/javascript">
16753 * @extends Roo.util.Observable
16754 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16755 * This class also supports single and multi selection modes. <br>
16756 * Create a data model bound view:
16758 var store = new Roo.data.Store(...);
16760 var view = new Roo.View({
16762 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16764 singleSelect: true,
16765 selectedClass: "ydataview-selected",
16769 // listen for node click?
16770 view.on("click", function(vw, index, node, e){
16771 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16775 dataModel.load("foobar.xml");
16777 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16779 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16780 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16782 * Note: old style constructor is still suported (container, template, config)
16785 * Create a new View
16786 * @param {Object} config The config object
16789 Roo.View = function(config, depreciated_tpl, depreciated_config){
16791 this.parent = false;
16793 if (typeof(depreciated_tpl) == 'undefined') {
16794 // new way.. - universal constructor.
16795 Roo.apply(this, config);
16796 this.el = Roo.get(this.el);
16799 this.el = Roo.get(config);
16800 this.tpl = depreciated_tpl;
16801 Roo.apply(this, depreciated_config);
16803 this.wrapEl = this.el.wrap().wrap();
16804 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16807 if(typeof(this.tpl) == "string"){
16808 this.tpl = new Roo.Template(this.tpl);
16810 // support xtype ctors..
16811 this.tpl = new Roo.factory(this.tpl, Roo);
16815 this.tpl.compile();
16820 * @event beforeclick
16821 * Fires before a click is processed. Returns false to cancel the default action.
16822 * @param {Roo.View} this
16823 * @param {Number} index The index of the target node
16824 * @param {HTMLElement} node The target node
16825 * @param {Roo.EventObject} e The raw event object
16827 "beforeclick" : true,
16830 * Fires when a template node is clicked.
16831 * @param {Roo.View} this
16832 * @param {Number} index The index of the target node
16833 * @param {HTMLElement} node The target node
16834 * @param {Roo.EventObject} e The raw event object
16839 * Fires when a template node is double clicked.
16840 * @param {Roo.View} this
16841 * @param {Number} index The index of the target node
16842 * @param {HTMLElement} node The target node
16843 * @param {Roo.EventObject} e The raw event object
16847 * @event contextmenu
16848 * Fires when a template node is right clicked.
16849 * @param {Roo.View} this
16850 * @param {Number} index The index of the target node
16851 * @param {HTMLElement} node The target node
16852 * @param {Roo.EventObject} e The raw event object
16854 "contextmenu" : true,
16856 * @event selectionchange
16857 * Fires when the selected nodes change.
16858 * @param {Roo.View} this
16859 * @param {Array} selections Array of the selected nodes
16861 "selectionchange" : true,
16864 * @event beforeselect
16865 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16866 * @param {Roo.View} this
16867 * @param {HTMLElement} node The node to be selected
16868 * @param {Array} selections Array of currently selected nodes
16870 "beforeselect" : true,
16872 * @event preparedata
16873 * Fires on every row to render, to allow you to change the data.
16874 * @param {Roo.View} this
16875 * @param {Object} data to be rendered (change this)
16877 "preparedata" : true
16885 "click": this.onClick,
16886 "dblclick": this.onDblClick,
16887 "contextmenu": this.onContextMenu,
16891 this.selections = [];
16893 this.cmp = new Roo.CompositeElementLite([]);
16895 this.store = Roo.factory(this.store, Roo.data);
16896 this.setStore(this.store, true);
16899 if ( this.footer && this.footer.xtype) {
16901 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16903 this.footer.dataSource = this.store;
16904 this.footer.container = fctr;
16905 this.footer = Roo.factory(this.footer, Roo);
16906 fctr.insertFirst(this.el);
16908 // this is a bit insane - as the paging toolbar seems to detach the el..
16909 // dom.parentNode.parentNode.parentNode
16910 // they get detached?
16914 Roo.View.superclass.constructor.call(this);
16919 Roo.extend(Roo.View, Roo.util.Observable, {
16922 * @cfg {Roo.data.Store} store Data store to load data from.
16927 * @cfg {String|Roo.Element} el The container element.
16932 * @cfg {String|Roo.Template} tpl The template used by this View
16936 * @cfg {String} dataName the named area of the template to use as the data area
16937 * Works with domtemplates roo-name="name"
16941 * @cfg {String} selectedClass The css class to add to selected nodes
16943 selectedClass : "x-view-selected",
16945 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16950 * @cfg {String} text to display on mask (default Loading)
16954 * @cfg {Boolean} multiSelect Allow multiple selection
16956 multiSelect : false,
16958 * @cfg {Boolean} singleSelect Allow single selection
16960 singleSelect: false,
16963 * @cfg {Boolean} toggleSelect - selecting
16965 toggleSelect : false,
16968 * @cfg {Boolean} tickable - selecting
16973 * Returns the element this view is bound to.
16974 * @return {Roo.Element}
16976 getEl : function(){
16977 return this.wrapEl;
16983 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16985 refresh : function(){
16986 //Roo.log('refresh');
16989 // if we are using something like 'domtemplate', then
16990 // the what gets used is:
16991 // t.applySubtemplate(NAME, data, wrapping data..)
16992 // the outer template then get' applied with
16993 // the store 'extra data'
16994 // and the body get's added to the
16995 // roo-name="data" node?
16996 // <span class='roo-tpl-{name}'></span> ?????
17000 this.clearSelections();
17001 this.el.update("");
17003 var records = this.store.getRange();
17004 if(records.length < 1) {
17006 // is this valid?? = should it render a template??
17008 this.el.update(this.emptyText);
17012 if (this.dataName) {
17013 this.el.update(t.apply(this.store.meta)); //????
17014 el = this.el.child('.roo-tpl-' + this.dataName);
17017 for(var i = 0, len = records.length; i < len; i++){
17018 var data = this.prepareData(records[i].data, i, records[i]);
17019 this.fireEvent("preparedata", this, data, i, records[i]);
17021 var d = Roo.apply({}, data);
17024 Roo.apply(d, {'roo-id' : Roo.id()});
17028 Roo.each(this.parent.item, function(item){
17029 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17032 Roo.apply(d, {'roo-data-checked' : 'checked'});
17036 html[html.length] = Roo.util.Format.trim(
17038 t.applySubtemplate(this.dataName, d, this.store.meta) :
17045 el.update(html.join(""));
17046 this.nodes = el.dom.childNodes;
17047 this.updateIndexes(0);
17052 * Function to override to reformat the data that is sent to
17053 * the template for each node.
17054 * DEPRICATED - use the preparedata event handler.
17055 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17056 * a JSON object for an UpdateManager bound view).
17058 prepareData : function(data, index, record)
17060 this.fireEvent("preparedata", this, data, index, record);
17064 onUpdate : function(ds, record){
17065 // Roo.log('on update');
17066 this.clearSelections();
17067 var index = this.store.indexOf(record);
17068 var n = this.nodes[index];
17069 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17070 n.parentNode.removeChild(n);
17071 this.updateIndexes(index, index);
17077 onAdd : function(ds, records, index)
17079 //Roo.log(['on Add', ds, records, index] );
17080 this.clearSelections();
17081 if(this.nodes.length == 0){
17085 var n = this.nodes[index];
17086 for(var i = 0, len = records.length; i < len; i++){
17087 var d = this.prepareData(records[i].data, i, records[i]);
17089 this.tpl.insertBefore(n, d);
17092 this.tpl.append(this.el, d);
17095 this.updateIndexes(index);
17098 onRemove : function(ds, record, index){
17099 // Roo.log('onRemove');
17100 this.clearSelections();
17101 var el = this.dataName ?
17102 this.el.child('.roo-tpl-' + this.dataName) :
17105 el.dom.removeChild(this.nodes[index]);
17106 this.updateIndexes(index);
17110 * Refresh an individual node.
17111 * @param {Number} index
17113 refreshNode : function(index){
17114 this.onUpdate(this.store, this.store.getAt(index));
17117 updateIndexes : function(startIndex, endIndex){
17118 var ns = this.nodes;
17119 startIndex = startIndex || 0;
17120 endIndex = endIndex || ns.length - 1;
17121 for(var i = startIndex; i <= endIndex; i++){
17122 ns[i].nodeIndex = i;
17127 * Changes the data store this view uses and refresh the view.
17128 * @param {Store} store
17130 setStore : function(store, initial){
17131 if(!initial && this.store){
17132 this.store.un("datachanged", this.refresh);
17133 this.store.un("add", this.onAdd);
17134 this.store.un("remove", this.onRemove);
17135 this.store.un("update", this.onUpdate);
17136 this.store.un("clear", this.refresh);
17137 this.store.un("beforeload", this.onBeforeLoad);
17138 this.store.un("load", this.onLoad);
17139 this.store.un("loadexception", this.onLoad);
17143 store.on("datachanged", this.refresh, this);
17144 store.on("add", this.onAdd, this);
17145 store.on("remove", this.onRemove, this);
17146 store.on("update", this.onUpdate, this);
17147 store.on("clear", this.refresh, this);
17148 store.on("beforeload", this.onBeforeLoad, this);
17149 store.on("load", this.onLoad, this);
17150 store.on("loadexception", this.onLoad, this);
17158 * onbeforeLoad - masks the loading area.
17161 onBeforeLoad : function(store,opts)
17163 //Roo.log('onBeforeLoad');
17165 this.el.update("");
17167 this.el.mask(this.mask ? this.mask : "Loading" );
17169 onLoad : function ()
17176 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17177 * @param {HTMLElement} node
17178 * @return {HTMLElement} The template node
17180 findItemFromChild : function(node){
17181 var el = this.dataName ?
17182 this.el.child('.roo-tpl-' + this.dataName,true) :
17185 if(!node || node.parentNode == el){
17188 var p = node.parentNode;
17189 while(p && p != el){
17190 if(p.parentNode == el){
17199 onClick : function(e){
17200 var item = this.findItemFromChild(e.getTarget());
17202 var index = this.indexOf(item);
17203 if(this.onItemClick(item, index, e) !== false){
17204 this.fireEvent("click", this, index, item, e);
17207 this.clearSelections();
17212 onContextMenu : function(e){
17213 var item = this.findItemFromChild(e.getTarget());
17215 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17220 onDblClick : function(e){
17221 var item = this.findItemFromChild(e.getTarget());
17223 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17227 onItemClick : function(item, index, e)
17229 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17232 if (this.toggleSelect) {
17233 var m = this.isSelected(item) ? 'unselect' : 'select';
17236 _t[m](item, true, false);
17239 if(this.multiSelect || this.singleSelect){
17240 if(this.multiSelect && e.shiftKey && this.lastSelection){
17241 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17243 this.select(item, this.multiSelect && e.ctrlKey);
17244 this.lastSelection = item;
17247 if(!this.tickable){
17248 e.preventDefault();
17256 * Get the number of selected nodes.
17259 getSelectionCount : function(){
17260 return this.selections.length;
17264 * Get the currently selected nodes.
17265 * @return {Array} An array of HTMLElements
17267 getSelectedNodes : function(){
17268 return this.selections;
17272 * Get the indexes of the selected nodes.
17275 getSelectedIndexes : function(){
17276 var indexes = [], s = this.selections;
17277 for(var i = 0, len = s.length; i < len; i++){
17278 indexes.push(s[i].nodeIndex);
17284 * Clear all selections
17285 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17287 clearSelections : function(suppressEvent){
17288 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17289 this.cmp.elements = this.selections;
17290 this.cmp.removeClass(this.selectedClass);
17291 this.selections = [];
17292 if(!suppressEvent){
17293 this.fireEvent("selectionchange", this, this.selections);
17299 * Returns true if the passed node is selected
17300 * @param {HTMLElement/Number} node The node or node index
17301 * @return {Boolean}
17303 isSelected : function(node){
17304 var s = this.selections;
17308 node = this.getNode(node);
17309 return s.indexOf(node) !== -1;
17314 * @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
17315 * @param {Boolean} keepExisting (optional) true to keep existing selections
17316 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17318 select : function(nodeInfo, keepExisting, suppressEvent){
17319 if(nodeInfo instanceof Array){
17321 this.clearSelections(true);
17323 for(var i = 0, len = nodeInfo.length; i < len; i++){
17324 this.select(nodeInfo[i], true, true);
17328 var node = this.getNode(nodeInfo);
17329 if(!node || this.isSelected(node)){
17330 return; // already selected.
17333 this.clearSelections(true);
17336 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17337 Roo.fly(node).addClass(this.selectedClass);
17338 this.selections.push(node);
17339 if(!suppressEvent){
17340 this.fireEvent("selectionchange", this, this.selections);
17348 * @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
17349 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17350 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17352 unselect : function(nodeInfo, keepExisting, suppressEvent)
17354 if(nodeInfo instanceof Array){
17355 Roo.each(this.selections, function(s) {
17356 this.unselect(s, nodeInfo);
17360 var node = this.getNode(nodeInfo);
17361 if(!node || !this.isSelected(node)){
17362 //Roo.log("not selected");
17363 return; // not selected.
17367 Roo.each(this.selections, function(s) {
17369 Roo.fly(node).removeClass(this.selectedClass);
17376 this.selections= ns;
17377 this.fireEvent("selectionchange", this, this.selections);
17381 * Gets a template node.
17382 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17383 * @return {HTMLElement} The node or null if it wasn't found
17385 getNode : function(nodeInfo){
17386 if(typeof nodeInfo == "string"){
17387 return document.getElementById(nodeInfo);
17388 }else if(typeof nodeInfo == "number"){
17389 return this.nodes[nodeInfo];
17395 * Gets a range template nodes.
17396 * @param {Number} startIndex
17397 * @param {Number} endIndex
17398 * @return {Array} An array of nodes
17400 getNodes : function(start, end){
17401 var ns = this.nodes;
17402 start = start || 0;
17403 end = typeof end == "undefined" ? ns.length - 1 : end;
17406 for(var i = start; i <= end; i++){
17410 for(var i = start; i >= end; i--){
17418 * Finds the index of the passed node
17419 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17420 * @return {Number} The index of the node or -1
17422 indexOf : function(node){
17423 node = this.getNode(node);
17424 if(typeof node.nodeIndex == "number"){
17425 return node.nodeIndex;
17427 var ns = this.nodes;
17428 for(var i = 0, len = ns.length; i < len; i++){
17439 * based on jquery fullcalendar
17443 Roo.bootstrap = Roo.bootstrap || {};
17445 * @class Roo.bootstrap.Calendar
17446 * @extends Roo.bootstrap.Component
17447 * Bootstrap Calendar class
17448 * @cfg {Boolean} loadMask (true|false) default false
17449 * @cfg {Object} header generate the user specific header of the calendar, default false
17452 * Create a new Container
17453 * @param {Object} config The config object
17458 Roo.bootstrap.Calendar = function(config){
17459 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17463 * Fires when a date is selected
17464 * @param {DatePicker} this
17465 * @param {Date} date The selected date
17469 * @event monthchange
17470 * Fires when the displayed month changes
17471 * @param {DatePicker} this
17472 * @param {Date} date The selected month
17474 'monthchange': true,
17476 * @event evententer
17477 * Fires when mouse over an event
17478 * @param {Calendar} this
17479 * @param {event} Event
17481 'evententer': true,
17483 * @event eventleave
17484 * Fires when the mouse leaves an
17485 * @param {Calendar} this
17488 'eventleave': true,
17490 * @event eventclick
17491 * Fires when the mouse click an
17492 * @param {Calendar} this
17501 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17504 * @cfg {Number} startDay
17505 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17513 getAutoCreate : function(){
17516 var fc_button = function(name, corner, style, content ) {
17517 return Roo.apply({},{
17519 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17521 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17524 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17535 style : 'width:100%',
17542 cls : 'fc-header-left',
17544 fc_button('prev', 'left', 'arrow', '‹' ),
17545 fc_button('next', 'right', 'arrow', '›' ),
17546 { tag: 'span', cls: 'fc-header-space' },
17547 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17555 cls : 'fc-header-center',
17559 cls: 'fc-header-title',
17562 html : 'month / year'
17570 cls : 'fc-header-right',
17572 /* fc_button('month', 'left', '', 'month' ),
17573 fc_button('week', '', '', 'week' ),
17574 fc_button('day', 'right', '', 'day' )
17586 header = this.header;
17589 var cal_heads = function() {
17591 // fixme - handle this.
17593 for (var i =0; i < Date.dayNames.length; i++) {
17594 var d = Date.dayNames[i];
17597 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17598 html : d.substring(0,3)
17602 ret[0].cls += ' fc-first';
17603 ret[6].cls += ' fc-last';
17606 var cal_cell = function(n) {
17609 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17614 cls: 'fc-day-number',
17618 cls: 'fc-day-content',
17622 style: 'position: relative;' // height: 17px;
17634 var cal_rows = function() {
17637 for (var r = 0; r < 6; r++) {
17644 for (var i =0; i < Date.dayNames.length; i++) {
17645 var d = Date.dayNames[i];
17646 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17649 row.cn[0].cls+=' fc-first';
17650 row.cn[0].cn[0].style = 'min-height:90px';
17651 row.cn[6].cls+=' fc-last';
17655 ret[0].cls += ' fc-first';
17656 ret[4].cls += ' fc-prev-last';
17657 ret[5].cls += ' fc-last';
17664 cls: 'fc-border-separate',
17665 style : 'width:100%',
17673 cls : 'fc-first fc-last',
17691 cls : 'fc-content',
17692 style : "position: relative;",
17695 cls : 'fc-view fc-view-month fc-grid',
17696 style : 'position: relative',
17697 unselectable : 'on',
17700 cls : 'fc-event-container',
17701 style : 'position:absolute;z-index:8;top:0;left:0;'
17719 initEvents : function()
17722 throw "can not find store for calendar";
17728 style: "text-align:center",
17732 style: "background-color:white;width:50%;margin:250 auto",
17736 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17747 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17749 var size = this.el.select('.fc-content', true).first().getSize();
17750 this.maskEl.setSize(size.width, size.height);
17751 this.maskEl.enableDisplayMode("block");
17752 if(!this.loadMask){
17753 this.maskEl.hide();
17756 this.store = Roo.factory(this.store, Roo.data);
17757 this.store.on('load', this.onLoad, this);
17758 this.store.on('beforeload', this.onBeforeLoad, this);
17762 this.cells = this.el.select('.fc-day',true);
17763 //Roo.log(this.cells);
17764 this.textNodes = this.el.query('.fc-day-number');
17765 this.cells.addClassOnOver('fc-state-hover');
17767 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17768 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17769 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17770 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17772 this.on('monthchange', this.onMonthChange, this);
17774 this.update(new Date().clearTime());
17777 resize : function() {
17778 var sz = this.el.getSize();
17780 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17781 this.el.select('.fc-day-content div',true).setHeight(34);
17786 showPrevMonth : function(e){
17787 this.update(this.activeDate.add("mo", -1));
17789 showToday : function(e){
17790 this.update(new Date().clearTime());
17793 showNextMonth : function(e){
17794 this.update(this.activeDate.add("mo", 1));
17798 showPrevYear : function(){
17799 this.update(this.activeDate.add("y", -1));
17803 showNextYear : function(){
17804 this.update(this.activeDate.add("y", 1));
17809 update : function(date)
17811 var vd = this.activeDate;
17812 this.activeDate = date;
17813 // if(vd && this.el){
17814 // var t = date.getTime();
17815 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17816 // Roo.log('using add remove');
17818 // this.fireEvent('monthchange', this, date);
17820 // this.cells.removeClass("fc-state-highlight");
17821 // this.cells.each(function(c){
17822 // if(c.dateValue == t){
17823 // c.addClass("fc-state-highlight");
17824 // setTimeout(function(){
17825 // try{c.dom.firstChild.focus();}catch(e){}
17835 var days = date.getDaysInMonth();
17837 var firstOfMonth = date.getFirstDateOfMonth();
17838 var startingPos = firstOfMonth.getDay()-this.startDay;
17840 if(startingPos < this.startDay){
17844 var pm = date.add(Date.MONTH, -1);
17845 var prevStart = pm.getDaysInMonth()-startingPos;
17847 this.cells = this.el.select('.fc-day',true);
17848 this.textNodes = this.el.query('.fc-day-number');
17849 this.cells.addClassOnOver('fc-state-hover');
17851 var cells = this.cells.elements;
17852 var textEls = this.textNodes;
17854 Roo.each(cells, function(cell){
17855 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17858 days += startingPos;
17860 // convert everything to numbers so it's fast
17861 var day = 86400000;
17862 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17865 //Roo.log(prevStart);
17867 var today = new Date().clearTime().getTime();
17868 var sel = date.clearTime().getTime();
17869 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17870 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17871 var ddMatch = this.disabledDatesRE;
17872 var ddText = this.disabledDatesText;
17873 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17874 var ddaysText = this.disabledDaysText;
17875 var format = this.format;
17877 var setCellClass = function(cal, cell){
17881 //Roo.log('set Cell Class');
17883 var t = d.getTime();
17887 cell.dateValue = t;
17889 cell.className += " fc-today";
17890 cell.className += " fc-state-highlight";
17891 cell.title = cal.todayText;
17894 // disable highlight in other month..
17895 //cell.className += " fc-state-highlight";
17900 cell.className = " fc-state-disabled";
17901 cell.title = cal.minText;
17905 cell.className = " fc-state-disabled";
17906 cell.title = cal.maxText;
17910 if(ddays.indexOf(d.getDay()) != -1){
17911 cell.title = ddaysText;
17912 cell.className = " fc-state-disabled";
17915 if(ddMatch && format){
17916 var fvalue = d.dateFormat(format);
17917 if(ddMatch.test(fvalue)){
17918 cell.title = ddText.replace("%0", fvalue);
17919 cell.className = " fc-state-disabled";
17923 if (!cell.initialClassName) {
17924 cell.initialClassName = cell.dom.className;
17927 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17932 for(; i < startingPos; i++) {
17933 textEls[i].innerHTML = (++prevStart);
17934 d.setDate(d.getDate()+1);
17936 cells[i].className = "fc-past fc-other-month";
17937 setCellClass(this, cells[i]);
17942 for(; i < days; i++){
17943 intDay = i - startingPos + 1;
17944 textEls[i].innerHTML = (intDay);
17945 d.setDate(d.getDate()+1);
17947 cells[i].className = ''; // "x-date-active";
17948 setCellClass(this, cells[i]);
17952 for(; i < 42; i++) {
17953 textEls[i].innerHTML = (++extraDays);
17954 d.setDate(d.getDate()+1);
17956 cells[i].className = "fc-future fc-other-month";
17957 setCellClass(this, cells[i]);
17960 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17962 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17964 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17965 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17967 if(totalRows != 6){
17968 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17969 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17972 this.fireEvent('monthchange', this, date);
17976 if(!this.internalRender){
17977 var main = this.el.dom.firstChild;
17978 var w = main.offsetWidth;
17979 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17980 Roo.fly(main).setWidth(w);
17981 this.internalRender = true;
17982 // opera does not respect the auto grow header center column
17983 // then, after it gets a width opera refuses to recalculate
17984 // without a second pass
17985 if(Roo.isOpera && !this.secondPass){
17986 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17987 this.secondPass = true;
17988 this.update.defer(10, this, [date]);
17995 findCell : function(dt) {
17996 dt = dt.clearTime().getTime();
17998 this.cells.each(function(c){
17999 //Roo.log("check " +c.dateValue + '?=' + dt);
18000 if(c.dateValue == dt){
18010 findCells : function(ev) {
18011 var s = ev.start.clone().clearTime().getTime();
18013 var e= ev.end.clone().clearTime().getTime();
18016 this.cells.each(function(c){
18017 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18019 if(c.dateValue > e){
18022 if(c.dateValue < s){
18031 // findBestRow: function(cells)
18035 // for (var i =0 ; i < cells.length;i++) {
18036 // ret = Math.max(cells[i].rows || 0,ret);
18043 addItem : function(ev)
18045 // look for vertical location slot in
18046 var cells = this.findCells(ev);
18048 // ev.row = this.findBestRow(cells);
18050 // work out the location.
18054 for(var i =0; i < cells.length; i++) {
18056 cells[i].row = cells[0].row;
18059 cells[i].row = cells[i].row + 1;
18069 if (crow.start.getY() == cells[i].getY()) {
18071 crow.end = cells[i];
18088 cells[0].events.push(ev);
18090 this.calevents.push(ev);
18093 clearEvents: function() {
18095 if(!this.calevents){
18099 Roo.each(this.cells.elements, function(c){
18105 Roo.each(this.calevents, function(e) {
18106 Roo.each(e.els, function(el) {
18107 el.un('mouseenter' ,this.onEventEnter, this);
18108 el.un('mouseleave' ,this.onEventLeave, this);
18113 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18119 renderEvents: function()
18123 this.cells.each(function(c) {
18132 if(c.row != c.events.length){
18133 r = 4 - (4 - (c.row - c.events.length));
18136 c.events = ev.slice(0, r);
18137 c.more = ev.slice(r);
18139 if(c.more.length && c.more.length == 1){
18140 c.events.push(c.more.pop());
18143 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18147 this.cells.each(function(c) {
18149 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18152 for (var e = 0; e < c.events.length; e++){
18153 var ev = c.events[e];
18154 var rows = ev.rows;
18156 for(var i = 0; i < rows.length; i++) {
18158 // how many rows should it span..
18161 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18162 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18164 unselectable : "on",
18167 cls: 'fc-event-inner',
18171 // cls: 'fc-event-time',
18172 // html : cells.length > 1 ? '' : ev.time
18176 cls: 'fc-event-title',
18177 html : String.format('{0}', ev.title)
18184 cls: 'ui-resizable-handle ui-resizable-e',
18185 html : '  '
18192 cfg.cls += ' fc-event-start';
18194 if ((i+1) == rows.length) {
18195 cfg.cls += ' fc-event-end';
18198 var ctr = _this.el.select('.fc-event-container',true).first();
18199 var cg = ctr.createChild(cfg);
18201 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18202 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18204 var r = (c.more.length) ? 1 : 0;
18205 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18206 cg.setWidth(ebox.right - sbox.x -2);
18208 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18209 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18210 cg.on('click', _this.onEventClick, _this, ev);
18221 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18222 style : 'position: absolute',
18223 unselectable : "on",
18226 cls: 'fc-event-inner',
18230 cls: 'fc-event-title',
18238 cls: 'ui-resizable-handle ui-resizable-e',
18239 html : '  '
18245 var ctr = _this.el.select('.fc-event-container',true).first();
18246 var cg = ctr.createChild(cfg);
18248 var sbox = c.select('.fc-day-content',true).first().getBox();
18249 var ebox = c.select('.fc-day-content',true).first().getBox();
18251 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18252 cg.setWidth(ebox.right - sbox.x -2);
18254 cg.on('click', _this.onMoreEventClick, _this, c.more);
18264 onEventEnter: function (e, el,event,d) {
18265 this.fireEvent('evententer', this, el, event);
18268 onEventLeave: function (e, el,event,d) {
18269 this.fireEvent('eventleave', this, el, event);
18272 onEventClick: function (e, el,event,d) {
18273 this.fireEvent('eventclick', this, el, event);
18276 onMonthChange: function () {
18280 onMoreEventClick: function(e, el, more)
18284 this.calpopover.placement = 'right';
18285 this.calpopover.setTitle('More');
18287 this.calpopover.setContent('');
18289 var ctr = this.calpopover.el.select('.popover-content', true).first();
18291 Roo.each(more, function(m){
18293 cls : 'fc-event-hori fc-event-draggable',
18296 var cg = ctr.createChild(cfg);
18298 cg.on('click', _this.onEventClick, _this, m);
18301 this.calpopover.show(el);
18306 onLoad: function ()
18308 this.calevents = [];
18311 if(this.store.getCount() > 0){
18312 this.store.data.each(function(d){
18315 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18316 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18317 time : d.data.start_time,
18318 title : d.data.title,
18319 description : d.data.description,
18320 venue : d.data.venue
18325 this.renderEvents();
18327 if(this.calevents.length && this.loadMask){
18328 this.maskEl.hide();
18332 onBeforeLoad: function()
18334 this.clearEvents();
18336 this.maskEl.show();
18350 * @class Roo.bootstrap.Popover
18351 * @extends Roo.bootstrap.Component
18352 * Bootstrap Popover class
18353 * @cfg {String} html contents of the popover (or false to use children..)
18354 * @cfg {String} title of popover (or false to hide)
18355 * @cfg {String} placement how it is placed
18356 * @cfg {String} trigger click || hover (or false to trigger manually)
18357 * @cfg {String} over what (parent or false to trigger manually.)
18358 * @cfg {Number} delay - delay before showing
18361 * Create a new Popover
18362 * @param {Object} config The config object
18365 Roo.bootstrap.Popover = function(config){
18366 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18372 * After the popover show
18374 * @param {Roo.bootstrap.Popover} this
18379 * After the popover hide
18381 * @param {Roo.bootstrap.Popover} this
18387 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18389 title: 'Fill in a title',
18392 placement : 'right',
18393 trigger : 'hover', // hover
18399 can_build_overlaid : false,
18401 getChildContainer : function()
18403 return this.el.select('.popover-content',true).first();
18406 getAutoCreate : function(){
18409 cls : 'popover roo-dynamic',
18410 style: 'display:block',
18416 cls : 'popover-inner',
18420 cls: 'popover-title popover-header',
18424 cls : 'popover-content popover-body',
18435 setTitle: function(str)
18438 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18440 setContent: function(str)
18443 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18445 // as it get's added to the bottom of the page.
18446 onRender : function(ct, position)
18448 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18450 var cfg = Roo.apply({}, this.getAutoCreate());
18454 cfg.cls += ' ' + this.cls;
18457 cfg.style = this.style;
18459 //Roo.log("adding to ");
18460 this.el = Roo.get(document.body).createChild(cfg, position);
18461 // Roo.log(this.el);
18466 initEvents : function()
18468 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18469 this.el.enableDisplayMode('block');
18471 if (this.over === false) {
18474 if (this.triggers === false) {
18477 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18478 var triggers = this.trigger ? this.trigger.split(' ') : [];
18479 Roo.each(triggers, function(trigger) {
18481 if (trigger == 'click') {
18482 on_el.on('click', this.toggle, this);
18483 } else if (trigger != 'manual') {
18484 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18485 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18487 on_el.on(eventIn ,this.enter, this);
18488 on_el.on(eventOut, this.leave, this);
18499 toggle : function () {
18500 this.hoverState == 'in' ? this.leave() : this.enter();
18503 enter : function () {
18505 clearTimeout(this.timeout);
18507 this.hoverState = 'in';
18509 if (!this.delay || !this.delay.show) {
18514 this.timeout = setTimeout(function () {
18515 if (_t.hoverState == 'in') {
18518 }, this.delay.show)
18521 leave : function() {
18522 clearTimeout(this.timeout);
18524 this.hoverState = 'out';
18526 if (!this.delay || !this.delay.hide) {
18531 this.timeout = setTimeout(function () {
18532 if (_t.hoverState == 'out') {
18535 }, this.delay.hide)
18538 show : function (on_el)
18541 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18545 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18546 if (this.html !== false) {
18547 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18549 this.el.removeClass([
18550 'fade','top','bottom', 'left', 'right','in',
18551 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18553 if (!this.title.length) {
18554 this.el.select('.popover-title',true).hide();
18557 var placement = typeof this.placement == 'function' ?
18558 this.placement.call(this, this.el, on_el) :
18561 var autoToken = /\s?auto?\s?/i;
18562 var autoPlace = autoToken.test(placement);
18564 placement = placement.replace(autoToken, '') || 'top';
18568 //this.el.setXY([0,0]);
18570 this.el.dom.style.display='block';
18571 this.el.addClass(placement);
18573 //this.el.appendTo(on_el);
18575 var p = this.getPosition();
18576 var box = this.el.getBox();
18581 var align = Roo.bootstrap.Popover.alignment[placement];
18584 this.el.alignTo(on_el, align[0],align[1]);
18585 //var arrow = this.el.select('.arrow',true).first();
18586 //arrow.set(align[2],
18588 this.el.addClass('in');
18591 if (this.el.hasClass('fade')) {
18595 this.hoverState = 'in';
18597 this.fireEvent('show', this);
18602 this.el.setXY([0,0]);
18603 this.el.removeClass('in');
18605 this.hoverState = null;
18607 this.fireEvent('hide', this);
18612 Roo.bootstrap.Popover.alignment = {
18613 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18614 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18615 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18616 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18627 * @class Roo.bootstrap.Progress
18628 * @extends Roo.bootstrap.Component
18629 * Bootstrap Progress class
18630 * @cfg {Boolean} striped striped of the progress bar
18631 * @cfg {Boolean} active animated of the progress bar
18635 * Create a new Progress
18636 * @param {Object} config The config object
18639 Roo.bootstrap.Progress = function(config){
18640 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18643 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18648 getAutoCreate : function(){
18656 cfg.cls += ' progress-striped';
18660 cfg.cls += ' active';
18679 * @class Roo.bootstrap.ProgressBar
18680 * @extends Roo.bootstrap.Component
18681 * Bootstrap ProgressBar class
18682 * @cfg {Number} aria_valuenow aria-value now
18683 * @cfg {Number} aria_valuemin aria-value min
18684 * @cfg {Number} aria_valuemax aria-value max
18685 * @cfg {String} label label for the progress bar
18686 * @cfg {String} panel (success | info | warning | danger )
18687 * @cfg {String} role role of the progress bar
18688 * @cfg {String} sr_only text
18692 * Create a new ProgressBar
18693 * @param {Object} config The config object
18696 Roo.bootstrap.ProgressBar = function(config){
18697 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18700 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18704 aria_valuemax : 100,
18710 getAutoCreate : function()
18715 cls: 'progress-bar',
18716 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18728 cfg.role = this.role;
18731 if(this.aria_valuenow){
18732 cfg['aria-valuenow'] = this.aria_valuenow;
18735 if(this.aria_valuemin){
18736 cfg['aria-valuemin'] = this.aria_valuemin;
18739 if(this.aria_valuemax){
18740 cfg['aria-valuemax'] = this.aria_valuemax;
18743 if(this.label && !this.sr_only){
18744 cfg.html = this.label;
18748 cfg.cls += ' progress-bar-' + this.panel;
18754 update : function(aria_valuenow)
18756 this.aria_valuenow = aria_valuenow;
18758 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18773 * @class Roo.bootstrap.TabGroup
18774 * @extends Roo.bootstrap.Column
18775 * Bootstrap Column class
18776 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18777 * @cfg {Boolean} carousel true to make the group behave like a carousel
18778 * @cfg {Boolean} bullets show bullets for the panels
18779 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18780 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18781 * @cfg {Boolean} showarrow (true|false) show arrow default true
18784 * Create a new TabGroup
18785 * @param {Object} config The config object
18788 Roo.bootstrap.TabGroup = function(config){
18789 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18791 this.navId = Roo.id();
18794 Roo.bootstrap.TabGroup.register(this);
18798 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18801 transition : false,
18806 slideOnTouch : false,
18809 getAutoCreate : function()
18811 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18813 cfg.cls += ' tab-content';
18815 if (this.carousel) {
18816 cfg.cls += ' carousel slide';
18819 cls : 'carousel-inner',
18823 if(this.bullets && !Roo.isTouch){
18826 cls : 'carousel-bullets',
18830 if(this.bullets_cls){
18831 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18838 cfg.cn[0].cn.push(bullets);
18841 if(this.showarrow){
18842 cfg.cn[0].cn.push({
18844 class : 'carousel-arrow',
18848 class : 'carousel-prev',
18852 class : 'fa fa-chevron-left'
18858 class : 'carousel-next',
18862 class : 'fa fa-chevron-right'
18875 initEvents: function()
18877 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18878 // this.el.on("touchstart", this.onTouchStart, this);
18881 if(this.autoslide){
18884 this.slideFn = window.setInterval(function() {
18885 _this.showPanelNext();
18889 if(this.showarrow){
18890 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18891 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18897 // onTouchStart : function(e, el, o)
18899 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18903 // this.showPanelNext();
18907 getChildContainer : function()
18909 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18913 * register a Navigation item
18914 * @param {Roo.bootstrap.NavItem} the navitem to add
18916 register : function(item)
18918 this.tabs.push( item);
18919 item.navId = this.navId; // not really needed..
18924 getActivePanel : function()
18927 Roo.each(this.tabs, function(t) {
18937 getPanelByName : function(n)
18940 Roo.each(this.tabs, function(t) {
18941 if (t.tabId == n) {
18949 indexOfPanel : function(p)
18952 Roo.each(this.tabs, function(t,i) {
18953 if (t.tabId == p.tabId) {
18962 * show a specific panel
18963 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18964 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18966 showPanel : function (pan)
18968 if(this.transition || typeof(pan) == 'undefined'){
18969 Roo.log("waiting for the transitionend");
18973 if (typeof(pan) == 'number') {
18974 pan = this.tabs[pan];
18977 if (typeof(pan) == 'string') {
18978 pan = this.getPanelByName(pan);
18981 var cur = this.getActivePanel();
18984 Roo.log('pan or acitve pan is undefined');
18988 if (pan.tabId == this.getActivePanel().tabId) {
18992 if (false === cur.fireEvent('beforedeactivate')) {
18996 if(this.bullets > 0 && !Roo.isTouch){
18997 this.setActiveBullet(this.indexOfPanel(pan));
19000 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19002 //class="carousel-item carousel-item-next carousel-item-left"
19004 this.transition = true;
19005 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
19006 var lr = dir == 'next' ? 'left' : 'right';
19007 pan.el.addClass(dir); // or prev
19008 pan.el.addClass('carousel-item-' + dir); // or prev
19009 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19010 cur.el.addClass(lr); // or right
19011 pan.el.addClass(lr);
19012 cur.el.addClass('carousel-item-' +lr); // or right
19013 pan.el.addClass('carousel-item-' +lr);
19017 cur.el.on('transitionend', function() {
19018 Roo.log("trans end?");
19020 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19021 pan.setActive(true);
19023 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19024 cur.setActive(false);
19026 _this.transition = false;
19028 }, this, { single: true } );
19033 cur.setActive(false);
19034 pan.setActive(true);
19039 showPanelNext : function()
19041 var i = this.indexOfPanel(this.getActivePanel());
19043 if (i >= this.tabs.length - 1 && !this.autoslide) {
19047 if (i >= this.tabs.length - 1 && this.autoslide) {
19051 this.showPanel(this.tabs[i+1]);
19054 showPanelPrev : function()
19056 var i = this.indexOfPanel(this.getActivePanel());
19058 if (i < 1 && !this.autoslide) {
19062 if (i < 1 && this.autoslide) {
19063 i = this.tabs.length;
19066 this.showPanel(this.tabs[i-1]);
19070 addBullet: function()
19072 if(!this.bullets || Roo.isTouch){
19075 var ctr = this.el.select('.carousel-bullets',true).first();
19076 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19077 var bullet = ctr.createChild({
19078 cls : 'bullet bullet-' + i
19079 },ctr.dom.lastChild);
19084 bullet.on('click', (function(e, el, o, ii, t){
19086 e.preventDefault();
19088 this.showPanel(ii);
19090 if(this.autoslide && this.slideFn){
19091 clearInterval(this.slideFn);
19092 this.slideFn = window.setInterval(function() {
19093 _this.showPanelNext();
19097 }).createDelegate(this, [i, bullet], true));
19102 setActiveBullet : function(i)
19108 Roo.each(this.el.select('.bullet', true).elements, function(el){
19109 el.removeClass('selected');
19112 var bullet = this.el.select('.bullet-' + i, true).first();
19118 bullet.addClass('selected');
19129 Roo.apply(Roo.bootstrap.TabGroup, {
19133 * register a Navigation Group
19134 * @param {Roo.bootstrap.NavGroup} the navgroup to add
19136 register : function(navgrp)
19138 this.groups[navgrp.navId] = navgrp;
19142 * fetch a Navigation Group based on the navigation ID
19143 * if one does not exist , it will get created.
19144 * @param {string} the navgroup to add
19145 * @returns {Roo.bootstrap.NavGroup} the navgroup
19147 get: function(navId) {
19148 if (typeof(this.groups[navId]) == 'undefined') {
19149 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19151 return this.groups[navId] ;
19166 * @class Roo.bootstrap.TabPanel
19167 * @extends Roo.bootstrap.Component
19168 * Bootstrap TabPanel class
19169 * @cfg {Boolean} active panel active
19170 * @cfg {String} html panel content
19171 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19172 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19173 * @cfg {String} href click to link..
19177 * Create a new TabPanel
19178 * @param {Object} config The config object
19181 Roo.bootstrap.TabPanel = function(config){
19182 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19186 * Fires when the active status changes
19187 * @param {Roo.bootstrap.TabPanel} this
19188 * @param {Boolean} state the new state
19193 * @event beforedeactivate
19194 * Fires before a tab is de-activated - can be used to do validation on a form.
19195 * @param {Roo.bootstrap.TabPanel} this
19196 * @return {Boolean} false if there is an error
19199 'beforedeactivate': true
19202 this.tabId = this.tabId || Roo.id();
19206 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19214 getAutoCreate : function(){
19219 // item is needed for carousel - not sure if it has any effect otherwise
19220 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19221 html: this.html || ''
19225 cfg.cls += ' active';
19229 cfg.tabId = this.tabId;
19237 initEvents: function()
19239 var p = this.parent();
19241 this.navId = this.navId || p.navId;
19243 if (typeof(this.navId) != 'undefined') {
19244 // not really needed.. but just in case.. parent should be a NavGroup.
19245 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19249 var i = tg.tabs.length - 1;
19251 if(this.active && tg.bullets > 0 && i < tg.bullets){
19252 tg.setActiveBullet(i);
19256 this.el.on('click', this.onClick, this);
19259 this.el.on("touchstart", this.onTouchStart, this);
19260 this.el.on("touchmove", this.onTouchMove, this);
19261 this.el.on("touchend", this.onTouchEnd, this);
19266 onRender : function(ct, position)
19268 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19271 setActive : function(state)
19273 Roo.log("panel - set active " + this.tabId + "=" + state);
19275 this.active = state;
19277 this.el.removeClass('active');
19279 } else if (!this.el.hasClass('active')) {
19280 this.el.addClass('active');
19283 this.fireEvent('changed', this, state);
19286 onClick : function(e)
19288 e.preventDefault();
19290 if(!this.href.length){
19294 window.location.href = this.href;
19303 onTouchStart : function(e)
19305 this.swiping = false;
19307 this.startX = e.browserEvent.touches[0].clientX;
19308 this.startY = e.browserEvent.touches[0].clientY;
19311 onTouchMove : function(e)
19313 this.swiping = true;
19315 this.endX = e.browserEvent.touches[0].clientX;
19316 this.endY = e.browserEvent.touches[0].clientY;
19319 onTouchEnd : function(e)
19326 var tabGroup = this.parent();
19328 if(this.endX > this.startX){ // swiping right
19329 tabGroup.showPanelPrev();
19333 if(this.startX > this.endX){ // swiping left
19334 tabGroup.showPanelNext();
19353 * @class Roo.bootstrap.DateField
19354 * @extends Roo.bootstrap.Input
19355 * Bootstrap DateField class
19356 * @cfg {Number} weekStart default 0
19357 * @cfg {String} viewMode default empty, (months|years)
19358 * @cfg {String} minViewMode default empty, (months|years)
19359 * @cfg {Number} startDate default -Infinity
19360 * @cfg {Number} endDate default Infinity
19361 * @cfg {Boolean} todayHighlight default false
19362 * @cfg {Boolean} todayBtn default false
19363 * @cfg {Boolean} calendarWeeks default false
19364 * @cfg {Object} daysOfWeekDisabled default empty
19365 * @cfg {Boolean} singleMode default false (true | false)
19367 * @cfg {Boolean} keyboardNavigation default true
19368 * @cfg {String} language default en
19371 * Create a new DateField
19372 * @param {Object} config The config object
19375 Roo.bootstrap.DateField = function(config){
19376 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19380 * Fires when this field show.
19381 * @param {Roo.bootstrap.DateField} this
19382 * @param {Mixed} date The date value
19387 * Fires when this field hide.
19388 * @param {Roo.bootstrap.DateField} this
19389 * @param {Mixed} date The date value
19394 * Fires when select a date.
19395 * @param {Roo.bootstrap.DateField} this
19396 * @param {Mixed} date The date value
19400 * @event beforeselect
19401 * Fires when before select a date.
19402 * @param {Roo.bootstrap.DateField} this
19403 * @param {Mixed} date The date value
19405 beforeselect : true
19409 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19412 * @cfg {String} format
19413 * The default date format string which can be overriden for localization support. The format must be
19414 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19418 * @cfg {String} altFormats
19419 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19420 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19422 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19430 todayHighlight : false,
19436 keyboardNavigation: true,
19438 calendarWeeks: false,
19440 startDate: -Infinity,
19444 daysOfWeekDisabled: [],
19448 singleMode : false,
19450 UTCDate: function()
19452 return new Date(Date.UTC.apply(Date, arguments));
19455 UTCToday: function()
19457 var today = new Date();
19458 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19461 getDate: function() {
19462 var d = this.getUTCDate();
19463 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19466 getUTCDate: function() {
19470 setDate: function(d) {
19471 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19474 setUTCDate: function(d) {
19476 this.setValue(this.formatDate(this.date));
19479 onRender: function(ct, position)
19482 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19484 this.language = this.language || 'en';
19485 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19486 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19488 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19489 this.format = this.format || 'm/d/y';
19490 this.isInline = false;
19491 this.isInput = true;
19492 this.component = this.el.select('.add-on', true).first() || false;
19493 this.component = (this.component && this.component.length === 0) ? false : this.component;
19494 this.hasInput = this.component && this.inputEl().length;
19496 if (typeof(this.minViewMode === 'string')) {
19497 switch (this.minViewMode) {
19499 this.minViewMode = 1;
19502 this.minViewMode = 2;
19505 this.minViewMode = 0;
19510 if (typeof(this.viewMode === 'string')) {
19511 switch (this.viewMode) {
19524 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19526 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19528 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19530 this.picker().on('mousedown', this.onMousedown, this);
19531 this.picker().on('click', this.onClick, this);
19533 this.picker().addClass('datepicker-dropdown');
19535 this.startViewMode = this.viewMode;
19537 if(this.singleMode){
19538 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19539 v.setVisibilityMode(Roo.Element.DISPLAY);
19543 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19544 v.setStyle('width', '189px');
19548 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19549 if(!this.calendarWeeks){
19554 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19555 v.attr('colspan', function(i, val){
19556 return parseInt(val) + 1;
19561 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19563 this.setStartDate(this.startDate);
19564 this.setEndDate(this.endDate);
19566 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19573 if(this.isInline) {
19578 picker : function()
19580 return this.pickerEl;
19581 // return this.el.select('.datepicker', true).first();
19584 fillDow: function()
19586 var dowCnt = this.weekStart;
19595 if(this.calendarWeeks){
19603 while (dowCnt < this.weekStart + 7) {
19607 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19611 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19614 fillMonths: function()
19617 var months = this.picker().select('>.datepicker-months td', true).first();
19619 months.dom.innerHTML = '';
19625 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19628 months.createChild(month);
19635 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;
19637 if (this.date < this.startDate) {
19638 this.viewDate = new Date(this.startDate);
19639 } else if (this.date > this.endDate) {
19640 this.viewDate = new Date(this.endDate);
19642 this.viewDate = new Date(this.date);
19650 var d = new Date(this.viewDate),
19651 year = d.getUTCFullYear(),
19652 month = d.getUTCMonth(),
19653 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19654 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19655 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19656 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19657 currentDate = this.date && this.date.valueOf(),
19658 today = this.UTCToday();
19660 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19662 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19664 // this.picker.select('>tfoot th.today').
19665 // .text(dates[this.language].today)
19666 // .toggle(this.todayBtn !== false);
19668 this.updateNavArrows();
19671 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19673 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19675 prevMonth.setUTCDate(day);
19677 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19679 var nextMonth = new Date(prevMonth);
19681 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19683 nextMonth = nextMonth.valueOf();
19685 var fillMonths = false;
19687 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19689 while(prevMonth.valueOf() <= nextMonth) {
19692 if (prevMonth.getUTCDay() === this.weekStart) {
19694 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19702 if(this.calendarWeeks){
19703 // ISO 8601: First week contains first thursday.
19704 // ISO also states week starts on Monday, but we can be more abstract here.
19706 // Start of current week: based on weekstart/current date
19707 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19708 // Thursday of this week
19709 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19710 // First Thursday of year, year from thursday
19711 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19712 // Calendar week: ms between thursdays, div ms per day, div 7 days
19713 calWeek = (th - yth) / 864e5 / 7 + 1;
19715 fillMonths.cn.push({
19723 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19725 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19728 if (this.todayHighlight &&
19729 prevMonth.getUTCFullYear() == today.getFullYear() &&
19730 prevMonth.getUTCMonth() == today.getMonth() &&
19731 prevMonth.getUTCDate() == today.getDate()) {
19732 clsName += ' today';
19735 if (currentDate && prevMonth.valueOf() === currentDate) {
19736 clsName += ' active';
19739 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19740 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19741 clsName += ' disabled';
19744 fillMonths.cn.push({
19746 cls: 'day ' + clsName,
19747 html: prevMonth.getDate()
19750 prevMonth.setDate(prevMonth.getDate()+1);
19753 var currentYear = this.date && this.date.getUTCFullYear();
19754 var currentMonth = this.date && this.date.getUTCMonth();
19756 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19758 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19759 v.removeClass('active');
19761 if(currentYear === year && k === currentMonth){
19762 v.addClass('active');
19765 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19766 v.addClass('disabled');
19772 year = parseInt(year/10, 10) * 10;
19774 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19776 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19779 for (var i = -1; i < 11; i++) {
19780 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19782 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19790 showMode: function(dir)
19793 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19796 Roo.each(this.picker().select('>div',true).elements, function(v){
19797 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19800 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19805 if(this.isInline) {
19809 this.picker().removeClass(['bottom', 'top']);
19811 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19813 * place to the top of element!
19817 this.picker().addClass('top');
19818 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19823 this.picker().addClass('bottom');
19825 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19828 parseDate : function(value)
19830 if(!value || value instanceof Date){
19833 var v = Date.parseDate(value, this.format);
19834 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19835 v = Date.parseDate(value, 'Y-m-d');
19837 if(!v && this.altFormats){
19838 if(!this.altFormatsArray){
19839 this.altFormatsArray = this.altFormats.split("|");
19841 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19842 v = Date.parseDate(value, this.altFormatsArray[i]);
19848 formatDate : function(date, fmt)
19850 return (!date || !(date instanceof Date)) ?
19851 date : date.dateFormat(fmt || this.format);
19854 onFocus : function()
19856 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19860 onBlur : function()
19862 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19864 var d = this.inputEl().getValue();
19871 showPopup : function()
19873 this.picker().show();
19877 this.fireEvent('showpopup', this, this.date);
19880 hidePopup : function()
19882 if(this.isInline) {
19885 this.picker().hide();
19886 this.viewMode = this.startViewMode;
19889 this.fireEvent('hidepopup', this, this.date);
19893 onMousedown: function(e)
19895 e.stopPropagation();
19896 e.preventDefault();
19901 Roo.bootstrap.DateField.superclass.keyup.call(this);
19905 setValue: function(v)
19907 if(this.fireEvent('beforeselect', this, v) !== false){
19908 var d = new Date(this.parseDate(v) ).clearTime();
19910 if(isNaN(d.getTime())){
19911 this.date = this.viewDate = '';
19912 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19916 v = this.formatDate(d);
19918 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19920 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19924 this.fireEvent('select', this, this.date);
19928 getValue: function()
19930 return this.formatDate(this.date);
19933 fireKey: function(e)
19935 if (!this.picker().isVisible()){
19936 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19942 var dateChanged = false,
19944 newDate, newViewDate;
19949 e.preventDefault();
19953 if (!this.keyboardNavigation) {
19956 dir = e.keyCode == 37 ? -1 : 1;
19959 newDate = this.moveYear(this.date, dir);
19960 newViewDate = this.moveYear(this.viewDate, dir);
19961 } else if (e.shiftKey){
19962 newDate = this.moveMonth(this.date, dir);
19963 newViewDate = this.moveMonth(this.viewDate, dir);
19965 newDate = new Date(this.date);
19966 newDate.setUTCDate(this.date.getUTCDate() + dir);
19967 newViewDate = new Date(this.viewDate);
19968 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19970 if (this.dateWithinRange(newDate)){
19971 this.date = newDate;
19972 this.viewDate = newViewDate;
19973 this.setValue(this.formatDate(this.date));
19975 e.preventDefault();
19976 dateChanged = true;
19981 if (!this.keyboardNavigation) {
19984 dir = e.keyCode == 38 ? -1 : 1;
19986 newDate = this.moveYear(this.date, dir);
19987 newViewDate = this.moveYear(this.viewDate, dir);
19988 } else if (e.shiftKey){
19989 newDate = this.moveMonth(this.date, dir);
19990 newViewDate = this.moveMonth(this.viewDate, dir);
19992 newDate = new Date(this.date);
19993 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19994 newViewDate = new Date(this.viewDate);
19995 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19997 if (this.dateWithinRange(newDate)){
19998 this.date = newDate;
19999 this.viewDate = newViewDate;
20000 this.setValue(this.formatDate(this.date));
20002 e.preventDefault();
20003 dateChanged = true;
20007 this.setValue(this.formatDate(this.date));
20009 e.preventDefault();
20012 this.setValue(this.formatDate(this.date));
20026 onClick: function(e)
20028 e.stopPropagation();
20029 e.preventDefault();
20031 var target = e.getTarget();
20033 if(target.nodeName.toLowerCase() === 'i'){
20034 target = Roo.get(target).dom.parentNode;
20037 var nodeName = target.nodeName;
20038 var className = target.className;
20039 var html = target.innerHTML;
20040 //Roo.log(nodeName);
20042 switch(nodeName.toLowerCase()) {
20044 switch(className) {
20050 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20051 switch(this.viewMode){
20053 this.viewDate = this.moveMonth(this.viewDate, dir);
20057 this.viewDate = this.moveYear(this.viewDate, dir);
20063 var date = new Date();
20064 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20066 this.setValue(this.formatDate(this.date));
20073 if (className.indexOf('disabled') < 0) {
20074 this.viewDate.setUTCDate(1);
20075 if (className.indexOf('month') > -1) {
20076 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20078 var year = parseInt(html, 10) || 0;
20079 this.viewDate.setUTCFullYear(year);
20083 if(this.singleMode){
20084 this.setValue(this.formatDate(this.viewDate));
20095 //Roo.log(className);
20096 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20097 var day = parseInt(html, 10) || 1;
20098 var year = this.viewDate.getUTCFullYear(),
20099 month = this.viewDate.getUTCMonth();
20101 if (className.indexOf('old') > -1) {
20108 } else if (className.indexOf('new') > -1) {
20116 //Roo.log([year,month,day]);
20117 this.date = this.UTCDate(year, month, day,0,0,0,0);
20118 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20120 //Roo.log(this.formatDate(this.date));
20121 this.setValue(this.formatDate(this.date));
20128 setStartDate: function(startDate)
20130 this.startDate = startDate || -Infinity;
20131 if (this.startDate !== -Infinity) {
20132 this.startDate = this.parseDate(this.startDate);
20135 this.updateNavArrows();
20138 setEndDate: function(endDate)
20140 this.endDate = endDate || Infinity;
20141 if (this.endDate !== Infinity) {
20142 this.endDate = this.parseDate(this.endDate);
20145 this.updateNavArrows();
20148 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20150 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20151 if (typeof(this.daysOfWeekDisabled) !== 'object') {
20152 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20154 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20155 return parseInt(d, 10);
20158 this.updateNavArrows();
20161 updateNavArrows: function()
20163 if(this.singleMode){
20167 var d = new Date(this.viewDate),
20168 year = d.getUTCFullYear(),
20169 month = d.getUTCMonth();
20171 Roo.each(this.picker().select('.prev', true).elements, function(v){
20173 switch (this.viewMode) {
20176 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20182 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20189 Roo.each(this.picker().select('.next', true).elements, function(v){
20191 switch (this.viewMode) {
20194 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20200 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20208 moveMonth: function(date, dir)
20213 var new_date = new Date(date.valueOf()),
20214 day = new_date.getUTCDate(),
20215 month = new_date.getUTCMonth(),
20216 mag = Math.abs(dir),
20218 dir = dir > 0 ? 1 : -1;
20221 // If going back one month, make sure month is not current month
20222 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20224 return new_date.getUTCMonth() == month;
20226 // If going forward one month, make sure month is as expected
20227 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20229 return new_date.getUTCMonth() != new_month;
20231 new_month = month + dir;
20232 new_date.setUTCMonth(new_month);
20233 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20234 if (new_month < 0 || new_month > 11) {
20235 new_month = (new_month + 12) % 12;
20238 // For magnitudes >1, move one month at a time...
20239 for (var i=0; i<mag; i++) {
20240 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20241 new_date = this.moveMonth(new_date, dir);
20243 // ...then reset the day, keeping it in the new month
20244 new_month = new_date.getUTCMonth();
20245 new_date.setUTCDate(day);
20247 return new_month != new_date.getUTCMonth();
20250 // Common date-resetting loop -- if date is beyond end of month, make it
20253 new_date.setUTCDate(--day);
20254 new_date.setUTCMonth(new_month);
20259 moveYear: function(date, dir)
20261 return this.moveMonth(date, dir*12);
20264 dateWithinRange: function(date)
20266 return date >= this.startDate && date <= this.endDate;
20272 this.picker().remove();
20275 validateValue : function(value)
20277 if(this.getVisibilityEl().hasClass('hidden')){
20281 if(value.length < 1) {
20282 if(this.allowBlank){
20288 if(value.length < this.minLength){
20291 if(value.length > this.maxLength){
20295 var vt = Roo.form.VTypes;
20296 if(!vt[this.vtype](value, this)){
20300 if(typeof this.validator == "function"){
20301 var msg = this.validator(value);
20307 if(this.regex && !this.regex.test(value)){
20311 if(typeof(this.parseDate(value)) == 'undefined'){
20315 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20319 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20329 this.date = this.viewDate = '';
20331 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20336 Roo.apply(Roo.bootstrap.DateField, {
20347 html: '<i class="fa fa-arrow-left"/>'
20357 html: '<i class="fa fa-arrow-right"/>'
20399 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20400 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20401 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20402 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20403 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20416 navFnc: 'FullYear',
20421 navFnc: 'FullYear',
20426 Roo.apply(Roo.bootstrap.DateField, {
20430 cls: 'datepicker dropdown-menu roo-dynamic',
20434 cls: 'datepicker-days',
20438 cls: 'table-condensed',
20440 Roo.bootstrap.DateField.head,
20444 Roo.bootstrap.DateField.footer
20451 cls: 'datepicker-months',
20455 cls: 'table-condensed',
20457 Roo.bootstrap.DateField.head,
20458 Roo.bootstrap.DateField.content,
20459 Roo.bootstrap.DateField.footer
20466 cls: 'datepicker-years',
20470 cls: 'table-condensed',
20472 Roo.bootstrap.DateField.head,
20473 Roo.bootstrap.DateField.content,
20474 Roo.bootstrap.DateField.footer
20493 * @class Roo.bootstrap.TimeField
20494 * @extends Roo.bootstrap.Input
20495 * Bootstrap DateField class
20499 * Create a new TimeField
20500 * @param {Object} config The config object
20503 Roo.bootstrap.TimeField = function(config){
20504 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20508 * Fires when this field show.
20509 * @param {Roo.bootstrap.DateField} thisthis
20510 * @param {Mixed} date The date value
20515 * Fires when this field hide.
20516 * @param {Roo.bootstrap.DateField} this
20517 * @param {Mixed} date The date value
20522 * Fires when select a date.
20523 * @param {Roo.bootstrap.DateField} this
20524 * @param {Mixed} date The date value
20530 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20533 * @cfg {String} format
20534 * The default time format string which can be overriden for localization support. The format must be
20535 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20539 onRender: function(ct, position)
20542 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20544 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20546 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20548 this.pop = this.picker().select('>.datepicker-time',true).first();
20549 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20551 this.picker().on('mousedown', this.onMousedown, this);
20552 this.picker().on('click', this.onClick, this);
20554 this.picker().addClass('datepicker-dropdown');
20559 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20560 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20561 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20562 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20563 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20564 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20568 fireKey: function(e){
20569 if (!this.picker().isVisible()){
20570 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20576 e.preventDefault();
20584 this.onTogglePeriod();
20587 this.onIncrementMinutes();
20590 this.onDecrementMinutes();
20599 onClick: function(e) {
20600 e.stopPropagation();
20601 e.preventDefault();
20604 picker : function()
20606 return this.el.select('.datepicker', true).first();
20609 fillTime: function()
20611 var time = this.pop.select('tbody', true).first();
20613 time.dom.innerHTML = '';
20628 cls: 'hours-up glyphicon glyphicon-chevron-up'
20648 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20669 cls: 'timepicker-hour',
20684 cls: 'timepicker-minute',
20699 cls: 'btn btn-primary period',
20721 cls: 'hours-down glyphicon glyphicon-chevron-down'
20741 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20759 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20766 var hours = this.time.getHours();
20767 var minutes = this.time.getMinutes();
20780 hours = hours - 12;
20784 hours = '0' + hours;
20788 minutes = '0' + minutes;
20791 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20792 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20793 this.pop.select('button', true).first().dom.innerHTML = period;
20799 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20801 var cls = ['bottom'];
20803 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20810 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20815 this.picker().addClass(cls.join('-'));
20819 Roo.each(cls, function(c){
20821 _this.picker().setTop(_this.inputEl().getHeight());
20825 _this.picker().setTop(0 - _this.picker().getHeight());
20830 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20834 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20841 onFocus : function()
20843 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20847 onBlur : function()
20849 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20855 this.picker().show();
20860 this.fireEvent('show', this, this.date);
20865 this.picker().hide();
20868 this.fireEvent('hide', this, this.date);
20871 setTime : function()
20874 this.setValue(this.time.format(this.format));
20876 this.fireEvent('select', this, this.date);
20881 onMousedown: function(e){
20882 e.stopPropagation();
20883 e.preventDefault();
20886 onIncrementHours: function()
20888 Roo.log('onIncrementHours');
20889 this.time = this.time.add(Date.HOUR, 1);
20894 onDecrementHours: function()
20896 Roo.log('onDecrementHours');
20897 this.time = this.time.add(Date.HOUR, -1);
20901 onIncrementMinutes: function()
20903 Roo.log('onIncrementMinutes');
20904 this.time = this.time.add(Date.MINUTE, 1);
20908 onDecrementMinutes: function()
20910 Roo.log('onDecrementMinutes');
20911 this.time = this.time.add(Date.MINUTE, -1);
20915 onTogglePeriod: function()
20917 Roo.log('onTogglePeriod');
20918 this.time = this.time.add(Date.HOUR, 12);
20925 Roo.apply(Roo.bootstrap.TimeField, {
20955 cls: 'btn btn-info ok',
20967 Roo.apply(Roo.bootstrap.TimeField, {
20971 cls: 'datepicker dropdown-menu',
20975 cls: 'datepicker-time',
20979 cls: 'table-condensed',
20981 Roo.bootstrap.TimeField.content,
20982 Roo.bootstrap.TimeField.footer
21001 * @class Roo.bootstrap.MonthField
21002 * @extends Roo.bootstrap.Input
21003 * Bootstrap MonthField class
21005 * @cfg {String} language default en
21008 * Create a new MonthField
21009 * @param {Object} config The config object
21012 Roo.bootstrap.MonthField = function(config){
21013 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21018 * Fires when this field show.
21019 * @param {Roo.bootstrap.MonthField} this
21020 * @param {Mixed} date The date value
21025 * Fires when this field hide.
21026 * @param {Roo.bootstrap.MonthField} this
21027 * @param {Mixed} date The date value
21032 * Fires when select a date.
21033 * @param {Roo.bootstrap.MonthField} this
21034 * @param {String} oldvalue The old value
21035 * @param {String} newvalue The new value
21041 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
21043 onRender: function(ct, position)
21046 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21048 this.language = this.language || 'en';
21049 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21050 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21052 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21053 this.isInline = false;
21054 this.isInput = true;
21055 this.component = this.el.select('.add-on', true).first() || false;
21056 this.component = (this.component && this.component.length === 0) ? false : this.component;
21057 this.hasInput = this.component && this.inputEL().length;
21059 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21061 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21063 this.picker().on('mousedown', this.onMousedown, this);
21064 this.picker().on('click', this.onClick, this);
21066 this.picker().addClass('datepicker-dropdown');
21068 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21069 v.setStyle('width', '189px');
21076 if(this.isInline) {
21082 setValue: function(v, suppressEvent)
21084 var o = this.getValue();
21086 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21090 if(suppressEvent !== true){
21091 this.fireEvent('select', this, o, v);
21096 getValue: function()
21101 onClick: function(e)
21103 e.stopPropagation();
21104 e.preventDefault();
21106 var target = e.getTarget();
21108 if(target.nodeName.toLowerCase() === 'i'){
21109 target = Roo.get(target).dom.parentNode;
21112 var nodeName = target.nodeName;
21113 var className = target.className;
21114 var html = target.innerHTML;
21116 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21120 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21122 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21128 picker : function()
21130 return this.pickerEl;
21133 fillMonths: function()
21136 var months = this.picker().select('>.datepicker-months td', true).first();
21138 months.dom.innerHTML = '';
21144 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21147 months.createChild(month);
21156 if(typeof(this.vIndex) == 'undefined' && this.value.length){
21157 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21160 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21161 e.removeClass('active');
21163 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21164 e.addClass('active');
21171 if(this.isInline) {
21175 this.picker().removeClass(['bottom', 'top']);
21177 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21179 * place to the top of element!
21183 this.picker().addClass('top');
21184 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21189 this.picker().addClass('bottom');
21191 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21194 onFocus : function()
21196 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21200 onBlur : function()
21202 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21204 var d = this.inputEl().getValue();
21213 this.picker().show();
21214 this.picker().select('>.datepicker-months', true).first().show();
21218 this.fireEvent('show', this, this.date);
21223 if(this.isInline) {
21226 this.picker().hide();
21227 this.fireEvent('hide', this, this.date);
21231 onMousedown: function(e)
21233 e.stopPropagation();
21234 e.preventDefault();
21239 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21243 fireKey: function(e)
21245 if (!this.picker().isVisible()){
21246 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21257 e.preventDefault();
21261 dir = e.keyCode == 37 ? -1 : 1;
21263 this.vIndex = this.vIndex + dir;
21265 if(this.vIndex < 0){
21269 if(this.vIndex > 11){
21273 if(isNaN(this.vIndex)){
21277 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21283 dir = e.keyCode == 38 ? -1 : 1;
21285 this.vIndex = this.vIndex + dir * 4;
21287 if(this.vIndex < 0){
21291 if(this.vIndex > 11){
21295 if(isNaN(this.vIndex)){
21299 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21304 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21305 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21309 e.preventDefault();
21312 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21313 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21329 this.picker().remove();
21334 Roo.apply(Roo.bootstrap.MonthField, {
21353 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21354 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21359 Roo.apply(Roo.bootstrap.MonthField, {
21363 cls: 'datepicker dropdown-menu roo-dynamic',
21367 cls: 'datepicker-months',
21371 cls: 'table-condensed',
21373 Roo.bootstrap.DateField.content
21393 * @class Roo.bootstrap.CheckBox
21394 * @extends Roo.bootstrap.Input
21395 * Bootstrap CheckBox class
21397 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21398 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21399 * @cfg {String} boxLabel The text that appears beside the checkbox
21400 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21401 * @cfg {Boolean} checked initnal the element
21402 * @cfg {Boolean} inline inline the element (default false)
21403 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21404 * @cfg {String} tooltip label tooltip
21407 * Create a new CheckBox
21408 * @param {Object} config The config object
21411 Roo.bootstrap.CheckBox = function(config){
21412 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21417 * Fires when the element is checked or unchecked.
21418 * @param {Roo.bootstrap.CheckBox} this This input
21419 * @param {Boolean} checked The new checked value
21424 * Fires when the element is click.
21425 * @param {Roo.bootstrap.CheckBox} this This input
21432 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21434 inputType: 'checkbox',
21443 // checkbox success does not make any sense really..
21448 getAutoCreate : function()
21450 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21456 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21459 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21465 type : this.inputType,
21466 value : this.inputValue,
21467 cls : 'roo-' + this.inputType, //'form-box',
21468 placeholder : this.placeholder || ''
21472 if(this.inputType != 'radio'){
21476 cls : 'roo-hidden-value',
21477 value : this.checked ? this.inputValue : this.valueOff
21482 if (this.weight) { // Validity check?
21483 cfg.cls += " " + this.inputType + "-" + this.weight;
21486 if (this.disabled) {
21487 input.disabled=true;
21491 input.checked = this.checked;
21496 input.name = this.name;
21498 if(this.inputType != 'radio'){
21499 hidden.name = this.name;
21500 input.name = '_hidden_' + this.name;
21505 input.cls += ' input-' + this.size;
21510 ['xs','sm','md','lg'].map(function(size){
21511 if (settings[size]) {
21512 cfg.cls += ' col-' + size + '-' + settings[size];
21516 var inputblock = input;
21518 if (this.before || this.after) {
21521 cls : 'input-group',
21526 inputblock.cn.push({
21528 cls : 'input-group-addon',
21533 inputblock.cn.push(input);
21535 if(this.inputType != 'radio'){
21536 inputblock.cn.push(hidden);
21540 inputblock.cn.push({
21542 cls : 'input-group-addon',
21548 var boxLabelCfg = false;
21554 //'for': id, // box label is handled by onclick - so no for...
21556 html: this.boxLabel
21559 boxLabelCfg.tooltip = this.tooltip;
21565 if (align ==='left' && this.fieldLabel.length) {
21566 // Roo.log("left and has label");
21571 cls : 'control-label',
21572 html : this.fieldLabel
21583 cfg.cn[1].cn.push(boxLabelCfg);
21586 if(this.labelWidth > 12){
21587 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21590 if(this.labelWidth < 13 && this.labelmd == 0){
21591 this.labelmd = this.labelWidth;
21594 if(this.labellg > 0){
21595 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21596 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21599 if(this.labelmd > 0){
21600 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21601 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21604 if(this.labelsm > 0){
21605 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21606 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21609 if(this.labelxs > 0){
21610 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21611 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21614 } else if ( this.fieldLabel.length) {
21615 // Roo.log(" label");
21619 tag: this.boxLabel ? 'span' : 'label',
21621 cls: 'control-label box-input-label',
21622 //cls : 'input-group-addon',
21623 html : this.fieldLabel
21630 cfg.cn.push(boxLabelCfg);
21635 // Roo.log(" no label && no align");
21636 cfg.cn = [ inputblock ] ;
21638 cfg.cn.push(boxLabelCfg);
21646 if(this.inputType != 'radio'){
21647 cfg.cn.push(hidden);
21655 * return the real input element.
21657 inputEl: function ()
21659 return this.el.select('input.roo-' + this.inputType,true).first();
21661 hiddenEl: function ()
21663 return this.el.select('input.roo-hidden-value',true).first();
21666 labelEl: function()
21668 return this.el.select('label.control-label',true).first();
21670 /* depricated... */
21674 return this.labelEl();
21677 boxLabelEl: function()
21679 return this.el.select('label.box-label',true).first();
21682 initEvents : function()
21684 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21686 this.inputEl().on('click', this.onClick, this);
21688 if (this.boxLabel) {
21689 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21692 this.startValue = this.getValue();
21695 Roo.bootstrap.CheckBox.register(this);
21699 onClick : function(e)
21701 if(this.fireEvent('click', this, e) !== false){
21702 this.setChecked(!this.checked);
21707 setChecked : function(state,suppressEvent)
21709 this.startValue = this.getValue();
21711 if(this.inputType == 'radio'){
21713 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21714 e.dom.checked = false;
21717 this.inputEl().dom.checked = true;
21719 this.inputEl().dom.value = this.inputValue;
21721 if(suppressEvent !== true){
21722 this.fireEvent('check', this, true);
21730 this.checked = state;
21732 this.inputEl().dom.checked = state;
21735 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21737 if(suppressEvent !== true){
21738 this.fireEvent('check', this, state);
21744 getValue : function()
21746 if(this.inputType == 'radio'){
21747 return this.getGroupValue();
21750 return this.hiddenEl().dom.value;
21754 getGroupValue : function()
21756 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21760 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21763 setValue : function(v,suppressEvent)
21765 if(this.inputType == 'radio'){
21766 this.setGroupValue(v, suppressEvent);
21770 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21775 setGroupValue : function(v, suppressEvent)
21777 this.startValue = this.getValue();
21779 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21780 e.dom.checked = false;
21782 if(e.dom.value == v){
21783 e.dom.checked = true;
21787 if(suppressEvent !== true){
21788 this.fireEvent('check', this, true);
21796 validate : function()
21798 if(this.getVisibilityEl().hasClass('hidden')){
21804 (this.inputType == 'radio' && this.validateRadio()) ||
21805 (this.inputType == 'checkbox' && this.validateCheckbox())
21811 this.markInvalid();
21815 validateRadio : function()
21817 if(this.getVisibilityEl().hasClass('hidden')){
21821 if(this.allowBlank){
21827 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21828 if(!e.dom.checked){
21840 validateCheckbox : function()
21843 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21844 //return (this.getValue() == this.inputValue) ? true : false;
21847 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21855 for(var i in group){
21856 if(group[i].el.isVisible(true)){
21864 for(var i in group){
21869 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21876 * Mark this field as valid
21878 markValid : function()
21882 this.fireEvent('valid', this);
21884 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21887 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21894 if(this.inputType == 'radio'){
21895 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21896 var fg = e.findParent('.form-group', false, true);
21897 if (Roo.bootstrap.version == 3) {
21898 fg.removeClass([_this.invalidClass, _this.validClass]);
21899 fg.addClass(_this.validClass);
21901 fg.removeClass(['is-valid', 'is-invalid']);
21902 fg.addClass('is-valid');
21910 var fg = this.el.findParent('.form-group', false, true);
21911 if (Roo.bootstrap.version == 3) {
21912 fg.removeClass([this.invalidClass, this.validClass]);
21913 fg.addClass(this.validClass);
21915 fg.removeClass(['is-valid', 'is-invalid']);
21916 fg.addClass('is-valid');
21921 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21927 for(var i in group){
21928 var fg = group[i].el.findParent('.form-group', false, true);
21929 if (Roo.bootstrap.version == 3) {
21930 fg.removeClass([this.invalidClass, this.validClass]);
21931 fg.addClass(this.validClass);
21933 fg.removeClass(['is-valid', 'is-invalid']);
21934 fg.addClass('is-valid');
21940 * Mark this field as invalid
21941 * @param {String} msg The validation message
21943 markInvalid : function(msg)
21945 if(this.allowBlank){
21951 this.fireEvent('invalid', this, msg);
21953 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21956 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21960 label.markInvalid();
21963 if(this.inputType == 'radio'){
21965 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21966 var fg = e.findParent('.form-group', false, true);
21967 if (Roo.bootstrap.version == 3) {
21968 fg.removeClass([_this.invalidClass, _this.validClass]);
21969 fg.addClass(_this.invalidClass);
21971 fg.removeClass(['is-invalid', 'is-valid']);
21972 fg.addClass('is-invalid');
21980 var fg = this.el.findParent('.form-group', false, true);
21981 if (Roo.bootstrap.version == 3) {
21982 fg.removeClass([_this.invalidClass, _this.validClass]);
21983 fg.addClass(_this.invalidClass);
21985 fg.removeClass(['is-invalid', 'is-valid']);
21986 fg.addClass('is-invalid');
21991 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21997 for(var i in group){
21998 var fg = group[i].el.findParent('.form-group', false, true);
21999 if (Roo.bootstrap.version == 3) {
22000 fg.removeClass([_this.invalidClass, _this.validClass]);
22001 fg.addClass(_this.invalidClass);
22003 fg.removeClass(['is-invalid', 'is-valid']);
22004 fg.addClass('is-invalid');
22010 clearInvalid : function()
22012 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22014 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22016 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22018 if (label && label.iconEl) {
22019 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22020 label.iconEl.removeClass(['is-invalid', 'is-valid']);
22024 disable : function()
22026 if(this.inputType != 'radio'){
22027 Roo.bootstrap.CheckBox.superclass.disable.call(this);
22034 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22035 _this.getActionEl().addClass(this.disabledClass);
22036 e.dom.disabled = true;
22040 this.disabled = true;
22041 this.fireEvent("disable", this);
22045 enable : function()
22047 if(this.inputType != 'radio'){
22048 Roo.bootstrap.CheckBox.superclass.enable.call(this);
22055 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22056 _this.getActionEl().removeClass(this.disabledClass);
22057 e.dom.disabled = false;
22061 this.disabled = false;
22062 this.fireEvent("enable", this);
22066 setBoxLabel : function(v)
22071 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22077 Roo.apply(Roo.bootstrap.CheckBox, {
22082 * register a CheckBox Group
22083 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22085 register : function(checkbox)
22087 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22088 this.groups[checkbox.groupId] = {};
22091 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22095 this.groups[checkbox.groupId][checkbox.name] = checkbox;
22099 * fetch a CheckBox Group based on the group ID
22100 * @param {string} the group ID
22101 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22103 get: function(groupId) {
22104 if (typeof(this.groups[groupId]) == 'undefined') {
22108 return this.groups[groupId] ;
22121 * @class Roo.bootstrap.Radio
22122 * @extends Roo.bootstrap.Component
22123 * Bootstrap Radio class
22124 * @cfg {String} boxLabel - the label associated
22125 * @cfg {String} value - the value of radio
22128 * Create a new Radio
22129 * @param {Object} config The config object
22131 Roo.bootstrap.Radio = function(config){
22132 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22136 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22142 getAutoCreate : function()
22146 cls : 'form-group radio',
22151 html : this.boxLabel
22159 initEvents : function()
22161 this.parent().register(this);
22163 this.el.on('click', this.onClick, this);
22167 onClick : function(e)
22169 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22170 this.setChecked(true);
22174 setChecked : function(state, suppressEvent)
22176 this.parent().setValue(this.value, suppressEvent);
22180 setBoxLabel : function(v)
22185 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22200 * @class Roo.bootstrap.SecurePass
22201 * @extends Roo.bootstrap.Input
22202 * Bootstrap SecurePass class
22206 * Create a new SecurePass
22207 * @param {Object} config The config object
22210 Roo.bootstrap.SecurePass = function (config) {
22211 // these go here, so the translation tool can replace them..
22213 PwdEmpty: "Please type a password, and then retype it to confirm.",
22214 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22215 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22216 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22217 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22218 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22219 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22220 TooWeak: "Your password is Too Weak."
22222 this.meterLabel = "Password strength:";
22223 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22224 this.meterClass = [
22225 "roo-password-meter-tooweak",
22226 "roo-password-meter-weak",
22227 "roo-password-meter-medium",
22228 "roo-password-meter-strong",
22229 "roo-password-meter-grey"
22234 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22237 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22239 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22241 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22242 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22243 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22244 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22245 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22246 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22247 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22257 * @cfg {String/Object} Label for the strength meter (defaults to
22258 * 'Password strength:')
22263 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22264 * ['Weak', 'Medium', 'Strong'])
22267 pwdStrengths: false,
22280 initEvents: function ()
22282 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22284 if (this.el.is('input[type=password]') && Roo.isSafari) {
22285 this.el.on('keydown', this.SafariOnKeyDown, this);
22288 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22291 onRender: function (ct, position)
22293 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22294 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22295 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22297 this.trigger.createChild({
22302 cls: 'roo-password-meter-grey col-xs-12',
22305 //width: this.meterWidth + 'px'
22309 cls: 'roo-password-meter-text'
22315 if (this.hideTrigger) {
22316 this.trigger.setDisplayed(false);
22318 this.setSize(this.width || '', this.height || '');
22321 onDestroy: function ()
22323 if (this.trigger) {
22324 this.trigger.removeAllListeners();
22325 this.trigger.remove();
22328 this.wrap.remove();
22330 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22333 checkStrength: function ()
22335 var pwd = this.inputEl().getValue();
22336 if (pwd == this._lastPwd) {
22341 if (this.ClientSideStrongPassword(pwd)) {
22343 } else if (this.ClientSideMediumPassword(pwd)) {
22345 } else if (this.ClientSideWeakPassword(pwd)) {
22351 Roo.log('strength1: ' + strength);
22353 //var pm = this.trigger.child('div/div/div').dom;
22354 var pm = this.trigger.child('div/div');
22355 pm.removeClass(this.meterClass);
22356 pm.addClass(this.meterClass[strength]);
22359 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22361 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22363 this._lastPwd = pwd;
22367 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22369 this._lastPwd = '';
22371 var pm = this.trigger.child('div/div');
22372 pm.removeClass(this.meterClass);
22373 pm.addClass('roo-password-meter-grey');
22376 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22379 this.inputEl().dom.type='password';
22382 validateValue: function (value)
22385 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22388 if (value.length == 0) {
22389 if (this.allowBlank) {
22390 this.clearInvalid();
22394 this.markInvalid(this.errors.PwdEmpty);
22395 this.errorMsg = this.errors.PwdEmpty;
22403 if ('[\x21-\x7e]*'.match(value)) {
22404 this.markInvalid(this.errors.PwdBadChar);
22405 this.errorMsg = this.errors.PwdBadChar;
22408 if (value.length < 6) {
22409 this.markInvalid(this.errors.PwdShort);
22410 this.errorMsg = this.errors.PwdShort;
22413 if (value.length > 16) {
22414 this.markInvalid(this.errors.PwdLong);
22415 this.errorMsg = this.errors.PwdLong;
22419 if (this.ClientSideStrongPassword(value)) {
22421 } else if (this.ClientSideMediumPassword(value)) {
22423 } else if (this.ClientSideWeakPassword(value)) {
22430 if (strength < 2) {
22431 //this.markInvalid(this.errors.TooWeak);
22432 this.errorMsg = this.errors.TooWeak;
22437 console.log('strength2: ' + strength);
22439 //var pm = this.trigger.child('div/div/div').dom;
22441 var pm = this.trigger.child('div/div');
22442 pm.removeClass(this.meterClass);
22443 pm.addClass(this.meterClass[strength]);
22445 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22447 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22449 this.errorMsg = '';
22453 CharacterSetChecks: function (type)
22456 this.fResult = false;
22459 isctype: function (character, type)
22462 case this.kCapitalLetter:
22463 if (character >= 'A' && character <= 'Z') {
22468 case this.kSmallLetter:
22469 if (character >= 'a' && character <= 'z') {
22475 if (character >= '0' && character <= '9') {
22480 case this.kPunctuation:
22481 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22492 IsLongEnough: function (pwd, size)
22494 return !(pwd == null || isNaN(size) || pwd.length < size);
22497 SpansEnoughCharacterSets: function (word, nb)
22499 if (!this.IsLongEnough(word, nb))
22504 var characterSetChecks = new Array(
22505 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22506 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22509 for (var index = 0; index < word.length; ++index) {
22510 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22511 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22512 characterSetChecks[nCharSet].fResult = true;
22519 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22520 if (characterSetChecks[nCharSet].fResult) {
22525 if (nCharSets < nb) {
22531 ClientSideStrongPassword: function (pwd)
22533 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22536 ClientSideMediumPassword: function (pwd)
22538 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22541 ClientSideWeakPassword: function (pwd)
22543 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22546 })//<script type="text/javascript">
22549 * Based Ext JS Library 1.1.1
22550 * Copyright(c) 2006-2007, Ext JS, LLC.
22556 * @class Roo.HtmlEditorCore
22557 * @extends Roo.Component
22558 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22560 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22563 Roo.HtmlEditorCore = function(config){
22566 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22571 * @event initialize
22572 * Fires when the editor is fully initialized (including the iframe)
22573 * @param {Roo.HtmlEditorCore} this
22578 * Fires when the editor is first receives the focus. Any insertion must wait
22579 * until after this event.
22580 * @param {Roo.HtmlEditorCore} this
22584 * @event beforesync
22585 * Fires before the textarea is updated with content from the editor iframe. Return false
22586 * to cancel the sync.
22587 * @param {Roo.HtmlEditorCore} this
22588 * @param {String} html
22592 * @event beforepush
22593 * Fires before the iframe editor is updated with content from the textarea. Return false
22594 * to cancel the push.
22595 * @param {Roo.HtmlEditorCore} this
22596 * @param {String} html
22601 * Fires when the textarea is updated with content from the editor iframe.
22602 * @param {Roo.HtmlEditorCore} this
22603 * @param {String} html
22608 * Fires when the iframe editor is updated with content from the textarea.
22609 * @param {Roo.HtmlEditorCore} this
22610 * @param {String} html
22615 * @event editorevent
22616 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22617 * @param {Roo.HtmlEditorCore} this
22623 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22625 // defaults : white / black...
22626 this.applyBlacklists();
22633 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22637 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22643 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22648 * @cfg {Number} height (in pixels)
22652 * @cfg {Number} width (in pixels)
22657 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22660 stylesheets: false,
22665 // private properties
22666 validationEvent : false,
22668 initialized : false,
22670 sourceEditMode : false,
22671 onFocus : Roo.emptyFn,
22673 hideMode:'offsets',
22677 // blacklist + whitelisted elements..
22684 * Protected method that will not generally be called directly. It
22685 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22686 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22688 getDocMarkup : function(){
22692 // inherit styels from page...??
22693 if (this.stylesheets === false) {
22695 Roo.get(document.head).select('style').each(function(node) {
22696 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22699 Roo.get(document.head).select('link').each(function(node) {
22700 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22703 } else if (!this.stylesheets.length) {
22705 st = '<style type="text/css">' +
22706 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22709 st = '<style type="text/css">' +
22714 st += '<style type="text/css">' +
22715 'IMG { cursor: pointer } ' +
22718 var cls = 'roo-htmleditor-body';
22720 if(this.bodyCls.length){
22721 cls += ' ' + this.bodyCls;
22724 return '<html><head>' + st +
22725 //<style type="text/css">' +
22726 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22728 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
22732 onRender : function(ct, position)
22735 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22736 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22739 this.el.dom.style.border = '0 none';
22740 this.el.dom.setAttribute('tabIndex', -1);
22741 this.el.addClass('x-hidden hide');
22745 if(Roo.isIE){ // fix IE 1px bogus margin
22746 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22750 this.frameId = Roo.id();
22754 var iframe = this.owner.wrap.createChild({
22756 cls: 'form-control', // bootstrap..
22758 name: this.frameId,
22759 frameBorder : 'no',
22760 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22765 this.iframe = iframe.dom;
22767 this.assignDocWin();
22769 this.doc.designMode = 'on';
22772 this.doc.write(this.getDocMarkup());
22776 var task = { // must defer to wait for browser to be ready
22778 //console.log("run task?" + this.doc.readyState);
22779 this.assignDocWin();
22780 if(this.doc.body || this.doc.readyState == 'complete'){
22782 this.doc.designMode="on";
22786 Roo.TaskMgr.stop(task);
22787 this.initEditor.defer(10, this);
22794 Roo.TaskMgr.start(task);
22799 onResize : function(w, h)
22801 Roo.log('resize: ' +w + ',' + h );
22802 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22806 if(typeof w == 'number'){
22808 this.iframe.style.width = w + 'px';
22810 if(typeof h == 'number'){
22812 this.iframe.style.height = h + 'px';
22814 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22821 * Toggles the editor between standard and source edit mode.
22822 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22824 toggleSourceEdit : function(sourceEditMode){
22826 this.sourceEditMode = sourceEditMode === true;
22828 if(this.sourceEditMode){
22830 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22833 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22834 //this.iframe.className = '';
22837 //this.setSize(this.owner.wrap.getSize());
22838 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22845 * Protected method that will not generally be called directly. If you need/want
22846 * custom HTML cleanup, this is the method you should override.
22847 * @param {String} html The HTML to be cleaned
22848 * return {String} The cleaned HTML
22850 cleanHtml : function(html){
22851 html = String(html);
22852 if(html.length > 5){
22853 if(Roo.isSafari){ // strip safari nonsense
22854 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22857 if(html == ' '){
22864 * HTML Editor -> Textarea
22865 * Protected method that will not generally be called directly. Syncs the contents
22866 * of the editor iframe with the textarea.
22868 syncValue : function(){
22869 if(this.initialized){
22870 var bd = (this.doc.body || this.doc.documentElement);
22871 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22872 var html = bd.innerHTML;
22874 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22875 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22877 html = '<div style="'+m[0]+'">' + html + '</div>';
22880 html = this.cleanHtml(html);
22881 // fix up the special chars.. normaly like back quotes in word...
22882 // however we do not want to do this with chinese..
22883 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22885 var cc = match.charCodeAt();
22887 // Get the character value, handling surrogate pairs
22888 if (match.length == 2) {
22889 // It's a surrogate pair, calculate the Unicode code point
22890 var high = match.charCodeAt(0) - 0xD800;
22891 var low = match.charCodeAt(1) - 0xDC00;
22892 cc = (high * 0x400) + low + 0x10000;
22894 (cc >= 0x4E00 && cc < 0xA000 ) ||
22895 (cc >= 0x3400 && cc < 0x4E00 ) ||
22896 (cc >= 0xf900 && cc < 0xfb00 )
22901 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22902 return "&#" + cc + ";";
22909 if(this.owner.fireEvent('beforesync', this, html) !== false){
22910 this.el.dom.value = html;
22911 this.owner.fireEvent('sync', this, html);
22917 * Protected method that will not generally be called directly. Pushes the value of the textarea
22918 * into the iframe editor.
22920 pushValue : function(){
22921 if(this.initialized){
22922 var v = this.el.dom.value.trim();
22924 // if(v.length < 1){
22928 if(this.owner.fireEvent('beforepush', this, v) !== false){
22929 var d = (this.doc.body || this.doc.documentElement);
22931 this.cleanUpPaste();
22932 this.el.dom.value = d.innerHTML;
22933 this.owner.fireEvent('push', this, v);
22939 deferFocus : function(){
22940 this.focus.defer(10, this);
22944 focus : function(){
22945 if(this.win && !this.sourceEditMode){
22952 assignDocWin: function()
22954 var iframe = this.iframe;
22957 this.doc = iframe.contentWindow.document;
22958 this.win = iframe.contentWindow;
22960 // if (!Roo.get(this.frameId)) {
22963 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22964 // this.win = Roo.get(this.frameId).dom.contentWindow;
22966 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22970 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22971 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22976 initEditor : function(){
22977 //console.log("INIT EDITOR");
22978 this.assignDocWin();
22982 this.doc.designMode="on";
22984 this.doc.write(this.getDocMarkup());
22987 var dbody = (this.doc.body || this.doc.documentElement);
22988 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22989 // this copies styles from the containing element into thsi one..
22990 // not sure why we need all of this..
22991 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22993 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22994 //ss['background-attachment'] = 'fixed'; // w3c
22995 dbody.bgProperties = 'fixed'; // ie
22996 //Roo.DomHelper.applyStyles(dbody, ss);
22997 Roo.EventManager.on(this.doc, {
22998 //'mousedown': this.onEditorEvent,
22999 'mouseup': this.onEditorEvent,
23000 'dblclick': this.onEditorEvent,
23001 'click': this.onEditorEvent,
23002 'keyup': this.onEditorEvent,
23007 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23009 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23010 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23012 this.initialized = true;
23014 this.owner.fireEvent('initialize', this);
23019 onDestroy : function(){
23025 //for (var i =0; i < this.toolbars.length;i++) {
23026 // // fixme - ask toolbars for heights?
23027 // this.toolbars[i].onDestroy();
23030 //this.wrap.dom.innerHTML = '';
23031 //this.wrap.remove();
23036 onFirstFocus : function(){
23038 this.assignDocWin();
23041 this.activated = true;
23044 if(Roo.isGecko){ // prevent silly gecko errors
23046 var s = this.win.getSelection();
23047 if(!s.focusNode || s.focusNode.nodeType != 3){
23048 var r = s.getRangeAt(0);
23049 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23054 this.execCmd('useCSS', true);
23055 this.execCmd('styleWithCSS', false);
23058 this.owner.fireEvent('activate', this);
23062 adjustFont: function(btn){
23063 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23064 //if(Roo.isSafari){ // safari
23067 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23068 if(Roo.isSafari){ // safari
23069 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23070 v = (v < 10) ? 10 : v;
23071 v = (v > 48) ? 48 : v;
23072 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23077 v = Math.max(1, v+adjust);
23079 this.execCmd('FontSize', v );
23082 onEditorEvent : function(e)
23084 this.owner.fireEvent('editorevent', this, e);
23085 // this.updateToolbar();
23086 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23089 insertTag : function(tg)
23091 // could be a bit smarter... -> wrap the current selected tRoo..
23092 if (tg.toLowerCase() == 'span' ||
23093 tg.toLowerCase() == 'code' ||
23094 tg.toLowerCase() == 'sup' ||
23095 tg.toLowerCase() == 'sub'
23098 range = this.createRange(this.getSelection());
23099 var wrappingNode = this.doc.createElement(tg.toLowerCase());
23100 wrappingNode.appendChild(range.extractContents());
23101 range.insertNode(wrappingNode);
23108 this.execCmd("formatblock", tg);
23112 insertText : function(txt)
23116 var range = this.createRange();
23117 range.deleteContents();
23118 //alert(Sender.getAttribute('label'));
23120 range.insertNode(this.doc.createTextNode(txt));
23126 * Executes a Midas editor command on the editor document and performs necessary focus and
23127 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23128 * @param {String} cmd The Midas command
23129 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23131 relayCmd : function(cmd, value){
23133 this.execCmd(cmd, value);
23134 this.owner.fireEvent('editorevent', this);
23135 //this.updateToolbar();
23136 this.owner.deferFocus();
23140 * Executes a Midas editor command directly on the editor document.
23141 * For visual commands, you should use {@link #relayCmd} instead.
23142 * <b>This should only be called after the editor is initialized.</b>
23143 * @param {String} cmd The Midas command
23144 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23146 execCmd : function(cmd, value){
23147 this.doc.execCommand(cmd, false, value === undefined ? null : value);
23154 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23156 * @param {String} text | dom node..
23158 insertAtCursor : function(text)
23161 if(!this.activated){
23167 var r = this.doc.selection.createRange();
23178 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23182 // from jquery ui (MIT licenced)
23184 var win = this.win;
23186 if (win.getSelection && win.getSelection().getRangeAt) {
23187 range = win.getSelection().getRangeAt(0);
23188 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23189 range.insertNode(node);
23190 } else if (win.document.selection && win.document.selection.createRange) {
23191 // no firefox support
23192 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23193 win.document.selection.createRange().pasteHTML(txt);
23195 // no firefox support
23196 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23197 this.execCmd('InsertHTML', txt);
23206 mozKeyPress : function(e){
23208 var c = e.getCharCode(), cmd;
23211 c = String.fromCharCode(c).toLowerCase();
23225 this.cleanUpPaste.defer(100, this);
23233 e.preventDefault();
23241 fixKeys : function(){ // load time branching for fastest keydown performance
23243 return function(e){
23244 var k = e.getKey(), r;
23247 r = this.doc.selection.createRange();
23250 r.pasteHTML('    ');
23257 r = this.doc.selection.createRange();
23259 var target = r.parentElement();
23260 if(!target || target.tagName.toLowerCase() != 'li'){
23262 r.pasteHTML('<br />');
23268 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23269 this.cleanUpPaste.defer(100, this);
23275 }else if(Roo.isOpera){
23276 return function(e){
23277 var k = e.getKey();
23281 this.execCmd('InsertHTML','    ');
23284 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23285 this.cleanUpPaste.defer(100, this);
23290 }else if(Roo.isSafari){
23291 return function(e){
23292 var k = e.getKey();
23296 this.execCmd('InsertText','\t');
23300 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23301 this.cleanUpPaste.defer(100, this);
23309 getAllAncestors: function()
23311 var p = this.getSelectedNode();
23314 a.push(p); // push blank onto stack..
23315 p = this.getParentElement();
23319 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23323 a.push(this.doc.body);
23327 lastSelNode : false,
23330 getSelection : function()
23332 this.assignDocWin();
23333 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23336 getSelectedNode: function()
23338 // this may only work on Gecko!!!
23340 // should we cache this!!!!
23345 var range = this.createRange(this.getSelection()).cloneRange();
23348 var parent = range.parentElement();
23350 var testRange = range.duplicate();
23351 testRange.moveToElementText(parent);
23352 if (testRange.inRange(range)) {
23355 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23358 parent = parent.parentElement;
23363 // is ancestor a text element.
23364 var ac = range.commonAncestorContainer;
23365 if (ac.nodeType == 3) {
23366 ac = ac.parentNode;
23369 var ar = ac.childNodes;
23372 var other_nodes = [];
23373 var has_other_nodes = false;
23374 for (var i=0;i<ar.length;i++) {
23375 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23378 // fullly contained node.
23380 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23385 // probably selected..
23386 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23387 other_nodes.push(ar[i]);
23391 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23396 has_other_nodes = true;
23398 if (!nodes.length && other_nodes.length) {
23399 nodes= other_nodes;
23401 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23407 createRange: function(sel)
23409 // this has strange effects when using with
23410 // top toolbar - not sure if it's a great idea.
23411 //this.editor.contentWindow.focus();
23412 if (typeof sel != "undefined") {
23414 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23416 return this.doc.createRange();
23419 return this.doc.createRange();
23422 getParentElement: function()
23425 this.assignDocWin();
23426 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23428 var range = this.createRange(sel);
23431 var p = range.commonAncestorContainer;
23432 while (p.nodeType == 3) { // text node
23443 * Range intersection.. the hard stuff...
23447 * [ -- selected range --- ]
23451 * if end is before start or hits it. fail.
23452 * if start is after end or hits it fail.
23454 * if either hits (but other is outside. - then it's not
23460 // @see http://www.thismuchiknow.co.uk/?p=64.
23461 rangeIntersectsNode : function(range, node)
23463 var nodeRange = node.ownerDocument.createRange();
23465 nodeRange.selectNode(node);
23467 nodeRange.selectNodeContents(node);
23470 var rangeStartRange = range.cloneRange();
23471 rangeStartRange.collapse(true);
23473 var rangeEndRange = range.cloneRange();
23474 rangeEndRange.collapse(false);
23476 var nodeStartRange = nodeRange.cloneRange();
23477 nodeStartRange.collapse(true);
23479 var nodeEndRange = nodeRange.cloneRange();
23480 nodeEndRange.collapse(false);
23482 return rangeStartRange.compareBoundaryPoints(
23483 Range.START_TO_START, nodeEndRange) == -1 &&
23484 rangeEndRange.compareBoundaryPoints(
23485 Range.START_TO_START, nodeStartRange) == 1;
23489 rangeCompareNode : function(range, node)
23491 var nodeRange = node.ownerDocument.createRange();
23493 nodeRange.selectNode(node);
23495 nodeRange.selectNodeContents(node);
23499 range.collapse(true);
23501 nodeRange.collapse(true);
23503 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23504 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23506 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23508 var nodeIsBefore = ss == 1;
23509 var nodeIsAfter = ee == -1;
23511 if (nodeIsBefore && nodeIsAfter) {
23514 if (!nodeIsBefore && nodeIsAfter) {
23515 return 1; //right trailed.
23518 if (nodeIsBefore && !nodeIsAfter) {
23519 return 2; // left trailed.
23525 // private? - in a new class?
23526 cleanUpPaste : function()
23528 // cleans up the whole document..
23529 Roo.log('cleanuppaste');
23531 this.cleanUpChildren(this.doc.body);
23532 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23533 if (clean != this.doc.body.innerHTML) {
23534 this.doc.body.innerHTML = clean;
23539 cleanWordChars : function(input) {// change the chars to hex code
23540 var he = Roo.HtmlEditorCore;
23542 var output = input;
23543 Roo.each(he.swapCodes, function(sw) {
23544 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23546 output = output.replace(swapper, sw[1]);
23553 cleanUpChildren : function (n)
23555 if (!n.childNodes.length) {
23558 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23559 this.cleanUpChild(n.childNodes[i]);
23566 cleanUpChild : function (node)
23569 //console.log(node);
23570 if (node.nodeName == "#text") {
23571 // clean up silly Windows -- stuff?
23574 if (node.nodeName == "#comment") {
23575 node.parentNode.removeChild(node);
23576 // clean up silly Windows -- stuff?
23579 var lcname = node.tagName.toLowerCase();
23580 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23581 // whitelist of tags..
23583 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23585 node.parentNode.removeChild(node);
23590 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23592 // spans with no attributes - just remove them..
23593 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23594 remove_keep_children = true;
23597 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23598 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23600 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23601 // remove_keep_children = true;
23604 if (remove_keep_children) {
23605 this.cleanUpChildren(node);
23606 // inserts everything just before this node...
23607 while (node.childNodes.length) {
23608 var cn = node.childNodes[0];
23609 node.removeChild(cn);
23610 node.parentNode.insertBefore(cn, node);
23612 node.parentNode.removeChild(node);
23616 if (!node.attributes || !node.attributes.length) {
23621 this.cleanUpChildren(node);
23625 function cleanAttr(n,v)
23628 if (v.match(/^\./) || v.match(/^\//)) {
23631 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23634 if (v.match(/^#/)) {
23637 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23638 node.removeAttribute(n);
23642 var cwhite = this.cwhite;
23643 var cblack = this.cblack;
23645 function cleanStyle(n,v)
23647 if (v.match(/expression/)) { //XSS?? should we even bother..
23648 node.removeAttribute(n);
23652 var parts = v.split(/;/);
23655 Roo.each(parts, function(p) {
23656 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23660 var l = p.split(':').shift().replace(/\s+/g,'');
23661 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23663 if ( cwhite.length && cblack.indexOf(l) > -1) {
23664 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23665 //node.removeAttribute(n);
23669 // only allow 'c whitelisted system attributes'
23670 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23671 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23672 //node.removeAttribute(n);
23682 if (clean.length) {
23683 node.setAttribute(n, clean.join(';'));
23685 node.removeAttribute(n);
23691 for (var i = node.attributes.length-1; i > -1 ; i--) {
23692 var a = node.attributes[i];
23695 if (a.name.toLowerCase().substr(0,2)=='on') {
23696 node.removeAttribute(a.name);
23699 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23700 node.removeAttribute(a.name);
23703 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23704 cleanAttr(a.name,a.value); // fixme..
23707 if (a.name == 'style') {
23708 cleanStyle(a.name,a.value);
23711 /// clean up MS crap..
23712 // tecnically this should be a list of valid class'es..
23715 if (a.name == 'class') {
23716 if (a.value.match(/^Mso/)) {
23717 node.removeAttribute('class');
23720 if (a.value.match(/^body$/)) {
23721 node.removeAttribute('class');
23732 this.cleanUpChildren(node);
23738 * Clean up MS wordisms...
23740 cleanWord : function(node)
23743 this.cleanWord(this.doc.body);
23748 node.nodeName == 'SPAN' &&
23749 !node.hasAttributes() &&
23750 node.childNodes.length == 1 &&
23751 node.firstChild.nodeName == "#text"
23753 var textNode = node.firstChild;
23754 node.removeChild(textNode);
23755 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23756 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23758 node.parentNode.insertBefore(textNode, node);
23759 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23760 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23762 node.parentNode.removeChild(node);
23765 if (node.nodeName == "#text") {
23766 // clean up silly Windows -- stuff?
23769 if (node.nodeName == "#comment") {
23770 node.parentNode.removeChild(node);
23771 // clean up silly Windows -- stuff?
23775 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23776 node.parentNode.removeChild(node);
23779 //Roo.log(node.tagName);
23780 // remove - but keep children..
23781 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23782 //Roo.log('-- removed');
23783 while (node.childNodes.length) {
23784 var cn = node.childNodes[0];
23785 node.removeChild(cn);
23786 node.parentNode.insertBefore(cn, node);
23787 // move node to parent - and clean it..
23788 this.cleanWord(cn);
23790 node.parentNode.removeChild(node);
23791 /// no need to iterate chidlren = it's got none..
23792 //this.iterateChildren(node, this.cleanWord);
23796 if (node.className.length) {
23798 var cn = node.className.split(/\W+/);
23800 Roo.each(cn, function(cls) {
23801 if (cls.match(/Mso[a-zA-Z]+/)) {
23806 node.className = cna.length ? cna.join(' ') : '';
23808 node.removeAttribute("class");
23812 if (node.hasAttribute("lang")) {
23813 node.removeAttribute("lang");
23816 if (node.hasAttribute("style")) {
23818 var styles = node.getAttribute("style").split(";");
23820 Roo.each(styles, function(s) {
23821 if (!s.match(/:/)) {
23824 var kv = s.split(":");
23825 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23828 // what ever is left... we allow.
23831 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23832 if (!nstyle.length) {
23833 node.removeAttribute('style');
23836 this.iterateChildren(node, this.cleanWord);
23842 * iterateChildren of a Node, calling fn each time, using this as the scole..
23843 * @param {DomNode} node node to iterate children of.
23844 * @param {Function} fn method of this class to call on each item.
23846 iterateChildren : function(node, fn)
23848 if (!node.childNodes.length) {
23851 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23852 fn.call(this, node.childNodes[i])
23858 * cleanTableWidths.
23860 * Quite often pasting from word etc.. results in tables with column and widths.
23861 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23864 cleanTableWidths : function(node)
23869 this.cleanTableWidths(this.doc.body);
23874 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23877 Roo.log(node.tagName);
23878 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23879 this.iterateChildren(node, this.cleanTableWidths);
23882 if (node.hasAttribute('width')) {
23883 node.removeAttribute('width');
23887 if (node.hasAttribute("style")) {
23890 var styles = node.getAttribute("style").split(";");
23892 Roo.each(styles, function(s) {
23893 if (!s.match(/:/)) {
23896 var kv = s.split(":");
23897 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23900 // what ever is left... we allow.
23903 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23904 if (!nstyle.length) {
23905 node.removeAttribute('style');
23909 this.iterateChildren(node, this.cleanTableWidths);
23917 domToHTML : function(currentElement, depth, nopadtext) {
23919 depth = depth || 0;
23920 nopadtext = nopadtext || false;
23922 if (!currentElement) {
23923 return this.domToHTML(this.doc.body);
23926 //Roo.log(currentElement);
23928 var allText = false;
23929 var nodeName = currentElement.nodeName;
23930 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23932 if (nodeName == '#text') {
23934 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23939 if (nodeName != 'BODY') {
23942 // Prints the node tagName, such as <A>, <IMG>, etc
23945 for(i = 0; i < currentElement.attributes.length;i++) {
23947 var aname = currentElement.attributes.item(i).name;
23948 if (!currentElement.attributes.item(i).value.length) {
23951 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23954 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23963 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23966 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23971 // Traverse the tree
23973 var currentElementChild = currentElement.childNodes.item(i);
23974 var allText = true;
23975 var innerHTML = '';
23977 while (currentElementChild) {
23978 // Formatting code (indent the tree so it looks nice on the screen)
23979 var nopad = nopadtext;
23980 if (lastnode == 'SPAN') {
23984 if (currentElementChild.nodeName == '#text') {
23985 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23986 toadd = nopadtext ? toadd : toadd.trim();
23987 if (!nopad && toadd.length > 80) {
23988 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23990 innerHTML += toadd;
23993 currentElementChild = currentElement.childNodes.item(i);
23999 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
24001 // Recursively traverse the tree structure of the child node
24002 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
24003 lastnode = currentElementChild.nodeName;
24005 currentElementChild=currentElement.childNodes.item(i);
24011 // The remaining code is mostly for formatting the tree
24012 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
24017 ret+= "</"+tagName+">";
24023 applyBlacklists : function()
24025 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
24026 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
24030 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24031 if (b.indexOf(tag) > -1) {
24034 this.white.push(tag);
24038 Roo.each(w, function(tag) {
24039 if (b.indexOf(tag) > -1) {
24042 if (this.white.indexOf(tag) > -1) {
24045 this.white.push(tag);
24050 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24051 if (w.indexOf(tag) > -1) {
24054 this.black.push(tag);
24058 Roo.each(b, function(tag) {
24059 if (w.indexOf(tag) > -1) {
24062 if (this.black.indexOf(tag) > -1) {
24065 this.black.push(tag);
24070 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
24071 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
24075 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24076 if (b.indexOf(tag) > -1) {
24079 this.cwhite.push(tag);
24083 Roo.each(w, function(tag) {
24084 if (b.indexOf(tag) > -1) {
24087 if (this.cwhite.indexOf(tag) > -1) {
24090 this.cwhite.push(tag);
24095 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24096 if (w.indexOf(tag) > -1) {
24099 this.cblack.push(tag);
24103 Roo.each(b, function(tag) {
24104 if (w.indexOf(tag) > -1) {
24107 if (this.cblack.indexOf(tag) > -1) {
24110 this.cblack.push(tag);
24115 setStylesheets : function(stylesheets)
24117 if(typeof(stylesheets) == 'string'){
24118 Roo.get(this.iframe.contentDocument.head).createChild({
24120 rel : 'stylesheet',
24129 Roo.each(stylesheets, function(s) {
24134 Roo.get(_this.iframe.contentDocument.head).createChild({
24136 rel : 'stylesheet',
24145 removeStylesheets : function()
24149 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24154 setStyle : function(style)
24156 Roo.get(this.iframe.contentDocument.head).createChild({
24165 // hide stuff that is not compatible
24179 * @event specialkey
24183 * @cfg {String} fieldClass @hide
24186 * @cfg {String} focusClass @hide
24189 * @cfg {String} autoCreate @hide
24192 * @cfg {String} inputType @hide
24195 * @cfg {String} invalidClass @hide
24198 * @cfg {String} invalidText @hide
24201 * @cfg {String} msgFx @hide
24204 * @cfg {String} validateOnBlur @hide
24208 Roo.HtmlEditorCore.white = [
24209 'area', 'br', 'img', 'input', 'hr', 'wbr',
24211 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24212 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24213 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24214 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24215 'table', 'ul', 'xmp',
24217 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24220 'dir', 'menu', 'ol', 'ul', 'dl',
24226 Roo.HtmlEditorCore.black = [
24227 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24229 'base', 'basefont', 'bgsound', 'blink', 'body',
24230 'frame', 'frameset', 'head', 'html', 'ilayer',
24231 'iframe', 'layer', 'link', 'meta', 'object',
24232 'script', 'style' ,'title', 'xml' // clean later..
24234 Roo.HtmlEditorCore.clean = [
24235 'script', 'style', 'title', 'xml'
24237 Roo.HtmlEditorCore.remove = [
24242 Roo.HtmlEditorCore.ablack = [
24246 Roo.HtmlEditorCore.aclean = [
24247 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24251 Roo.HtmlEditorCore.pwhite= [
24252 'http', 'https', 'mailto'
24255 // white listed style attributes.
24256 Roo.HtmlEditorCore.cwhite= [
24257 // 'text-align', /// default is to allow most things..
24263 // black listed style attributes.
24264 Roo.HtmlEditorCore.cblack= [
24265 // 'font-size' -- this can be set by the project
24269 Roo.HtmlEditorCore.swapCodes =[
24288 * @class Roo.bootstrap.HtmlEditor
24289 * @extends Roo.bootstrap.TextArea
24290 * Bootstrap HtmlEditor class
24293 * Create a new HtmlEditor
24294 * @param {Object} config The config object
24297 Roo.bootstrap.HtmlEditor = function(config){
24298 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24299 if (!this.toolbars) {
24300 this.toolbars = [];
24303 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24306 * @event initialize
24307 * Fires when the editor is fully initialized (including the iframe)
24308 * @param {HtmlEditor} this
24313 * Fires when the editor is first receives the focus. Any insertion must wait
24314 * until after this event.
24315 * @param {HtmlEditor} this
24319 * @event beforesync
24320 * Fires before the textarea is updated with content from the editor iframe. Return false
24321 * to cancel the sync.
24322 * @param {HtmlEditor} this
24323 * @param {String} html
24327 * @event beforepush
24328 * Fires before the iframe editor is updated with content from the textarea. Return false
24329 * to cancel the push.
24330 * @param {HtmlEditor} this
24331 * @param {String} html
24336 * Fires when the textarea is updated with content from the editor iframe.
24337 * @param {HtmlEditor} this
24338 * @param {String} html
24343 * Fires when the iframe editor is updated with content from the textarea.
24344 * @param {HtmlEditor} this
24345 * @param {String} html
24349 * @event editmodechange
24350 * Fires when the editor switches edit modes
24351 * @param {HtmlEditor} this
24352 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24354 editmodechange: true,
24356 * @event editorevent
24357 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24358 * @param {HtmlEditor} this
24362 * @event firstfocus
24363 * Fires when on first focus - needed by toolbars..
24364 * @param {HtmlEditor} this
24369 * Auto save the htmlEditor value as a file into Events
24370 * @param {HtmlEditor} this
24374 * @event savedpreview
24375 * preview the saved version of htmlEditor
24376 * @param {HtmlEditor} this
24383 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24387 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24392 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24397 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24402 * @cfg {Number} height (in pixels)
24406 * @cfg {Number} width (in pixels)
24411 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24414 stylesheets: false,
24419 // private properties
24420 validationEvent : false,
24422 initialized : false,
24425 onFocus : Roo.emptyFn,
24427 hideMode:'offsets',
24429 tbContainer : false,
24433 toolbarContainer :function() {
24434 return this.wrap.select('.x-html-editor-tb',true).first();
24438 * Protected method that will not generally be called directly. It
24439 * is called when the editor creates its toolbar. Override this method if you need to
24440 * add custom toolbar buttons.
24441 * @param {HtmlEditor} editor
24443 createToolbar : function(){
24444 Roo.log('renewing');
24445 Roo.log("create toolbars");
24447 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24448 this.toolbars[0].render(this.toolbarContainer());
24452 // if (!editor.toolbars || !editor.toolbars.length) {
24453 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24456 // for (var i =0 ; i < editor.toolbars.length;i++) {
24457 // editor.toolbars[i] = Roo.factory(
24458 // typeof(editor.toolbars[i]) == 'string' ?
24459 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24460 // Roo.bootstrap.HtmlEditor);
24461 // editor.toolbars[i].init(editor);
24467 onRender : function(ct, position)
24469 // Roo.log("Call onRender: " + this.xtype);
24471 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24473 this.wrap = this.inputEl().wrap({
24474 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24477 this.editorcore.onRender(ct, position);
24479 if (this.resizable) {
24480 this.resizeEl = new Roo.Resizable(this.wrap, {
24484 minHeight : this.height,
24485 height: this.height,
24486 handles : this.resizable,
24489 resize : function(r, w, h) {
24490 _t.onResize(w,h); // -something
24496 this.createToolbar(this);
24499 if(!this.width && this.resizable){
24500 this.setSize(this.wrap.getSize());
24502 if (this.resizeEl) {
24503 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24504 // should trigger onReize..
24510 onResize : function(w, h)
24512 Roo.log('resize: ' +w + ',' + h );
24513 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24517 if(this.inputEl() ){
24518 if(typeof w == 'number'){
24519 var aw = w - this.wrap.getFrameWidth('lr');
24520 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24523 if(typeof h == 'number'){
24524 var tbh = -11; // fixme it needs to tool bar size!
24525 for (var i =0; i < this.toolbars.length;i++) {
24526 // fixme - ask toolbars for heights?
24527 tbh += this.toolbars[i].el.getHeight();
24528 //if (this.toolbars[i].footer) {
24529 // tbh += this.toolbars[i].footer.el.getHeight();
24537 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24538 ah -= 5; // knock a few pixes off for look..
24539 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24543 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24544 this.editorcore.onResize(ew,eh);
24549 * Toggles the editor between standard and source edit mode.
24550 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24552 toggleSourceEdit : function(sourceEditMode)
24554 this.editorcore.toggleSourceEdit(sourceEditMode);
24556 if(this.editorcore.sourceEditMode){
24557 Roo.log('editor - showing textarea');
24560 // Roo.log(this.syncValue());
24562 this.inputEl().removeClass(['hide', 'x-hidden']);
24563 this.inputEl().dom.removeAttribute('tabIndex');
24564 this.inputEl().focus();
24566 Roo.log('editor - hiding textarea');
24568 // Roo.log(this.pushValue());
24571 this.inputEl().addClass(['hide', 'x-hidden']);
24572 this.inputEl().dom.setAttribute('tabIndex', -1);
24573 //this.deferFocus();
24576 if(this.resizable){
24577 this.setSize(this.wrap.getSize());
24580 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24583 // private (for BoxComponent)
24584 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24586 // private (for BoxComponent)
24587 getResizeEl : function(){
24591 // private (for BoxComponent)
24592 getPositionEl : function(){
24597 initEvents : function(){
24598 this.originalValue = this.getValue();
24602 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24605 // markInvalid : Roo.emptyFn,
24607 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24610 // clearInvalid : Roo.emptyFn,
24612 setValue : function(v){
24613 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24614 this.editorcore.pushValue();
24619 deferFocus : function(){
24620 this.focus.defer(10, this);
24624 focus : function(){
24625 this.editorcore.focus();
24631 onDestroy : function(){
24637 for (var i =0; i < this.toolbars.length;i++) {
24638 // fixme - ask toolbars for heights?
24639 this.toolbars[i].onDestroy();
24642 this.wrap.dom.innerHTML = '';
24643 this.wrap.remove();
24648 onFirstFocus : function(){
24649 //Roo.log("onFirstFocus");
24650 this.editorcore.onFirstFocus();
24651 for (var i =0; i < this.toolbars.length;i++) {
24652 this.toolbars[i].onFirstFocus();
24658 syncValue : function()
24660 this.editorcore.syncValue();
24663 pushValue : function()
24665 this.editorcore.pushValue();
24669 // hide stuff that is not compatible
24683 * @event specialkey
24687 * @cfg {String} fieldClass @hide
24690 * @cfg {String} focusClass @hide
24693 * @cfg {String} autoCreate @hide
24696 * @cfg {String} inputType @hide
24700 * @cfg {String} invalidText @hide
24703 * @cfg {String} msgFx @hide
24706 * @cfg {String} validateOnBlur @hide
24715 Roo.namespace('Roo.bootstrap.htmleditor');
24717 * @class Roo.bootstrap.HtmlEditorToolbar1
24723 new Roo.bootstrap.HtmlEditor({
24726 new Roo.bootstrap.HtmlEditorToolbar1({
24727 disable : { fonts: 1 , format: 1, ..., ... , ...],
24733 * @cfg {Object} disable List of elements to disable..
24734 * @cfg {Array} btns List of additional buttons.
24738 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24741 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24744 Roo.apply(this, config);
24746 // default disabled, based on 'good practice'..
24747 this.disable = this.disable || {};
24748 Roo.applyIf(this.disable, {
24751 specialElements : true
24753 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24755 this.editor = config.editor;
24756 this.editorcore = config.editor.editorcore;
24758 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24760 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24761 // dont call parent... till later.
24763 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24768 editorcore : false,
24773 "h1","h2","h3","h4","h5","h6",
24775 "abbr", "acronym", "address", "cite", "samp", "var",
24779 onRender : function(ct, position)
24781 // Roo.log("Call onRender: " + this.xtype);
24783 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24785 this.el.dom.style.marginBottom = '0';
24787 var editorcore = this.editorcore;
24788 var editor= this.editor;
24791 var btn = function(id,cmd , toggle, handler, html){
24793 var event = toggle ? 'toggle' : 'click';
24798 xns: Roo.bootstrap,
24802 enableToggle:toggle !== false,
24804 pressed : toggle ? false : null,
24807 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24808 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24814 // var cb_box = function...
24819 xns: Roo.bootstrap,
24824 xns: Roo.bootstrap,
24828 Roo.each(this.formats, function(f) {
24829 style.menu.items.push({
24831 xns: Roo.bootstrap,
24832 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24837 editorcore.insertTag(this.tagname);
24844 children.push(style);
24846 btn('bold',false,true);
24847 btn('italic',false,true);
24848 btn('align-left', 'justifyleft',true);
24849 btn('align-center', 'justifycenter',true);
24850 btn('align-right' , 'justifyright',true);
24851 btn('link', false, false, function(btn) {
24852 //Roo.log("create link?");
24853 var url = prompt(this.createLinkText, this.defaultLinkValue);
24854 if(url && url != 'http:/'+'/'){
24855 this.editorcore.relayCmd('createlink', url);
24858 btn('list','insertunorderedlist',true);
24859 btn('pencil', false,true, function(btn){
24861 this.toggleSourceEdit(btn.pressed);
24864 if (this.editor.btns.length > 0) {
24865 for (var i = 0; i<this.editor.btns.length; i++) {
24866 children.push(this.editor.btns[i]);
24874 xns: Roo.bootstrap,
24879 xns: Roo.bootstrap,
24884 cog.menu.items.push({
24886 xns: Roo.bootstrap,
24887 html : Clean styles,
24892 editorcore.insertTag(this.tagname);
24901 this.xtype = 'NavSimplebar';
24903 for(var i=0;i< children.length;i++) {
24905 this.buttons.add(this.addxtypeChild(children[i]));
24909 editor.on('editorevent', this.updateToolbar, this);
24911 onBtnClick : function(id)
24913 this.editorcore.relayCmd(id);
24914 this.editorcore.focus();
24918 * Protected method that will not generally be called directly. It triggers
24919 * a toolbar update by reading the markup state of the current selection in the editor.
24921 updateToolbar: function(){
24923 if(!this.editorcore.activated){
24924 this.editor.onFirstFocus(); // is this neeed?
24928 var btns = this.buttons;
24929 var doc = this.editorcore.doc;
24930 btns.get('bold').setActive(doc.queryCommandState('bold'));
24931 btns.get('italic').setActive(doc.queryCommandState('italic'));
24932 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24934 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24935 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24936 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24938 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24939 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24942 var ans = this.editorcore.getAllAncestors();
24943 if (this.formatCombo) {
24946 var store = this.formatCombo.store;
24947 this.formatCombo.setValue("");
24948 for (var i =0; i < ans.length;i++) {
24949 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24951 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24959 // hides menus... - so this cant be on a menu...
24960 Roo.bootstrap.MenuMgr.hideAll();
24962 Roo.bootstrap.MenuMgr.hideAll();
24963 //this.editorsyncValue();
24965 onFirstFocus: function() {
24966 this.buttons.each(function(item){
24970 toggleSourceEdit : function(sourceEditMode){
24973 if(sourceEditMode){
24974 Roo.log("disabling buttons");
24975 this.buttons.each( function(item){
24976 if(item.cmd != 'pencil'){
24982 Roo.log("enabling buttons");
24983 if(this.editorcore.initialized){
24984 this.buttons.each( function(item){
24990 Roo.log("calling toggole on editor");
24991 // tell the editor that it's been pressed..
24992 this.editor.toggleSourceEdit(sourceEditMode);
25002 * @class Roo.bootstrap.Table.AbstractSelectionModel
25003 * @extends Roo.util.Observable
25004 * Abstract base class for grid SelectionModels. It provides the interface that should be
25005 * implemented by descendant classes. This class should not be directly instantiated.
25008 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25009 this.locked = false;
25010 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25014 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
25015 /** @ignore Called by the grid automatically. Do not call directly. */
25016 init : function(grid){
25022 * Locks the selections.
25025 this.locked = true;
25029 * Unlocks the selections.
25031 unlock : function(){
25032 this.locked = false;
25036 * Returns true if the selections are locked.
25037 * @return {Boolean}
25039 isLocked : function(){
25040 return this.locked;
25044 initEvents : function ()
25050 * @extends Roo.bootstrap.Table.AbstractSelectionModel
25051 * @class Roo.bootstrap.Table.RowSelectionModel
25052 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25053 * It supports multiple selections and keyboard selection/navigation.
25055 * @param {Object} config
25058 Roo.bootstrap.Table.RowSelectionModel = function(config){
25059 Roo.apply(this, config);
25060 this.selections = new Roo.util.MixedCollection(false, function(o){
25065 this.lastActive = false;
25069 * @event selectionchange
25070 * Fires when the selection changes
25071 * @param {SelectionModel} this
25073 "selectionchange" : true,
25075 * @event afterselectionchange
25076 * Fires after the selection changes (eg. by key press or clicking)
25077 * @param {SelectionModel} this
25079 "afterselectionchange" : true,
25081 * @event beforerowselect
25082 * Fires when a row is selected being selected, return false to cancel.
25083 * @param {SelectionModel} this
25084 * @param {Number} rowIndex The selected index
25085 * @param {Boolean} keepExisting False if other selections will be cleared
25087 "beforerowselect" : true,
25090 * Fires when a row is selected.
25091 * @param {SelectionModel} this
25092 * @param {Number} rowIndex The selected index
25093 * @param {Roo.data.Record} r The record
25095 "rowselect" : true,
25097 * @event rowdeselect
25098 * Fires when a row is deselected.
25099 * @param {SelectionModel} this
25100 * @param {Number} rowIndex The selected index
25102 "rowdeselect" : true
25104 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25105 this.locked = false;
25108 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
25110 * @cfg {Boolean} singleSelect
25111 * True to allow selection of only one row at a time (defaults to false)
25113 singleSelect : false,
25116 initEvents : function()
25119 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25120 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
25121 //}else{ // allow click to work like normal
25122 // this.grid.on("rowclick", this.handleDragableRowClick, this);
25124 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25125 this.grid.on("rowclick", this.handleMouseDown, this);
25127 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25128 "up" : function(e){
25130 this.selectPrevious(e.shiftKey);
25131 }else if(this.last !== false && this.lastActive !== false){
25132 var last = this.last;
25133 this.selectRange(this.last, this.lastActive-1);
25134 this.grid.getView().focusRow(this.lastActive);
25135 if(last !== false){
25139 this.selectFirstRow();
25141 this.fireEvent("afterselectionchange", this);
25143 "down" : function(e){
25145 this.selectNext(e.shiftKey);
25146 }else if(this.last !== false && this.lastActive !== false){
25147 var last = this.last;
25148 this.selectRange(this.last, this.lastActive+1);
25149 this.grid.getView().focusRow(this.lastActive);
25150 if(last !== false){
25154 this.selectFirstRow();
25156 this.fireEvent("afterselectionchange", this);
25160 this.grid.store.on('load', function(){
25161 this.selections.clear();
25164 var view = this.grid.view;
25165 view.on("refresh", this.onRefresh, this);
25166 view.on("rowupdated", this.onRowUpdated, this);
25167 view.on("rowremoved", this.onRemove, this);
25172 onRefresh : function()
25174 var ds = this.grid.store, i, v = this.grid.view;
25175 var s = this.selections;
25176 s.each(function(r){
25177 if((i = ds.indexOfId(r.id)) != -1){
25186 onRemove : function(v, index, r){
25187 this.selections.remove(r);
25191 onRowUpdated : function(v, index, r){
25192 if(this.isSelected(r)){
25193 v.onRowSelect(index);
25199 * @param {Array} records The records to select
25200 * @param {Boolean} keepExisting (optional) True to keep existing selections
25202 selectRecords : function(records, keepExisting)
25205 this.clearSelections();
25207 var ds = this.grid.store;
25208 for(var i = 0, len = records.length; i < len; i++){
25209 this.selectRow(ds.indexOf(records[i]), true);
25214 * Gets the number of selected rows.
25217 getCount : function(){
25218 return this.selections.length;
25222 * Selects the first row in the grid.
25224 selectFirstRow : function(){
25229 * Select the last row.
25230 * @param {Boolean} keepExisting (optional) True to keep existing selections
25232 selectLastRow : function(keepExisting){
25233 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25234 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25238 * Selects the row immediately following the last selected row.
25239 * @param {Boolean} keepExisting (optional) True to keep existing selections
25241 selectNext : function(keepExisting)
25243 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25244 this.selectRow(this.last+1, keepExisting);
25245 this.grid.getView().focusRow(this.last);
25250 * Selects the row that precedes the last selected row.
25251 * @param {Boolean} keepExisting (optional) True to keep existing selections
25253 selectPrevious : function(keepExisting){
25255 this.selectRow(this.last-1, keepExisting);
25256 this.grid.getView().focusRow(this.last);
25261 * Returns the selected records
25262 * @return {Array} Array of selected records
25264 getSelections : function(){
25265 return [].concat(this.selections.items);
25269 * Returns the first selected record.
25272 getSelected : function(){
25273 return this.selections.itemAt(0);
25278 * Clears all selections.
25280 clearSelections : function(fast)
25286 var ds = this.grid.store;
25287 var s = this.selections;
25288 s.each(function(r){
25289 this.deselectRow(ds.indexOfId(r.id));
25293 this.selections.clear();
25300 * Selects all rows.
25302 selectAll : function(){
25306 this.selections.clear();
25307 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25308 this.selectRow(i, true);
25313 * Returns True if there is a selection.
25314 * @return {Boolean}
25316 hasSelection : function(){
25317 return this.selections.length > 0;
25321 * Returns True if the specified row is selected.
25322 * @param {Number/Record} record The record or index of the record to check
25323 * @return {Boolean}
25325 isSelected : function(index){
25326 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25327 return (r && this.selections.key(r.id) ? true : false);
25331 * Returns True if the specified record id is selected.
25332 * @param {String} id The id of record to check
25333 * @return {Boolean}
25335 isIdSelected : function(id){
25336 return (this.selections.key(id) ? true : false);
25341 handleMouseDBClick : function(e, t){
25345 handleMouseDown : function(e, t)
25347 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25348 if(this.isLocked() || rowIndex < 0 ){
25351 if(e.shiftKey && this.last !== false){
25352 var last = this.last;
25353 this.selectRange(last, rowIndex, e.ctrlKey);
25354 this.last = last; // reset the last
25358 var isSelected = this.isSelected(rowIndex);
25359 //Roo.log("select row:" + rowIndex);
25361 this.deselectRow(rowIndex);
25363 this.selectRow(rowIndex, true);
25367 if(e.button !== 0 && isSelected){
25368 alert('rowIndex 2: ' + rowIndex);
25369 view.focusRow(rowIndex);
25370 }else if(e.ctrlKey && isSelected){
25371 this.deselectRow(rowIndex);
25372 }else if(!isSelected){
25373 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25374 view.focusRow(rowIndex);
25378 this.fireEvent("afterselectionchange", this);
25381 handleDragableRowClick : function(grid, rowIndex, e)
25383 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25384 this.selectRow(rowIndex, false);
25385 grid.view.focusRow(rowIndex);
25386 this.fireEvent("afterselectionchange", this);
25391 * Selects multiple rows.
25392 * @param {Array} rows Array of the indexes of the row to select
25393 * @param {Boolean} keepExisting (optional) True to keep existing selections
25395 selectRows : function(rows, keepExisting){
25397 this.clearSelections();
25399 for(var i = 0, len = rows.length; i < len; i++){
25400 this.selectRow(rows[i], true);
25405 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25406 * @param {Number} startRow The index of the first row in the range
25407 * @param {Number} endRow The index of the last row in the range
25408 * @param {Boolean} keepExisting (optional) True to retain existing selections
25410 selectRange : function(startRow, endRow, keepExisting){
25415 this.clearSelections();
25417 if(startRow <= endRow){
25418 for(var i = startRow; i <= endRow; i++){
25419 this.selectRow(i, true);
25422 for(var i = startRow; i >= endRow; i--){
25423 this.selectRow(i, true);
25429 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25430 * @param {Number} startRow The index of the first row in the range
25431 * @param {Number} endRow The index of the last row in the range
25433 deselectRange : function(startRow, endRow, preventViewNotify){
25437 for(var i = startRow; i <= endRow; i++){
25438 this.deselectRow(i, preventViewNotify);
25444 * @param {Number} row The index of the row to select
25445 * @param {Boolean} keepExisting (optional) True to keep existing selections
25447 selectRow : function(index, keepExisting, preventViewNotify)
25449 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25452 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25453 if(!keepExisting || this.singleSelect){
25454 this.clearSelections();
25457 var r = this.grid.store.getAt(index);
25458 //console.log('selectRow - record id :' + r.id);
25460 this.selections.add(r);
25461 this.last = this.lastActive = index;
25462 if(!preventViewNotify){
25463 var proxy = new Roo.Element(
25464 this.grid.getRowDom(index)
25466 proxy.addClass('bg-info info');
25468 this.fireEvent("rowselect", this, index, r);
25469 this.fireEvent("selectionchange", this);
25475 * @param {Number} row The index of the row to deselect
25477 deselectRow : function(index, preventViewNotify)
25482 if(this.last == index){
25485 if(this.lastActive == index){
25486 this.lastActive = false;
25489 var r = this.grid.store.getAt(index);
25494 this.selections.remove(r);
25495 //.console.log('deselectRow - record id :' + r.id);
25496 if(!preventViewNotify){
25498 var proxy = new Roo.Element(
25499 this.grid.getRowDom(index)
25501 proxy.removeClass('bg-info info');
25503 this.fireEvent("rowdeselect", this, index);
25504 this.fireEvent("selectionchange", this);
25508 restoreLast : function(){
25510 this.last = this._last;
25515 acceptsNav : function(row, col, cm){
25516 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25520 onEditorKey : function(field, e){
25521 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25526 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25528 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25530 }else if(k == e.ENTER && !e.ctrlKey){
25534 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25536 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25538 }else if(k == e.ESC){
25542 g.startEditing(newCell[0], newCell[1]);
25548 * Ext JS Library 1.1.1
25549 * Copyright(c) 2006-2007, Ext JS, LLC.
25551 * Originally Released Under LGPL - original licence link has changed is not relivant.
25554 * <script type="text/javascript">
25558 * @class Roo.bootstrap.PagingToolbar
25559 * @extends Roo.bootstrap.NavSimplebar
25560 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25562 * Create a new PagingToolbar
25563 * @param {Object} config The config object
25564 * @param {Roo.data.Store} store
25566 Roo.bootstrap.PagingToolbar = function(config)
25568 // old args format still supported... - xtype is prefered..
25569 // created from xtype...
25571 this.ds = config.dataSource;
25573 if (config.store && !this.ds) {
25574 this.store= Roo.factory(config.store, Roo.data);
25575 this.ds = this.store;
25576 this.ds.xmodule = this.xmodule || false;
25579 this.toolbarItems = [];
25580 if (config.items) {
25581 this.toolbarItems = config.items;
25584 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25589 this.bind(this.ds);
25592 if (Roo.bootstrap.version == 4) {
25593 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25595 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25600 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25602 * @cfg {Roo.data.Store} dataSource
25603 * The underlying data store providing the paged data
25606 * @cfg {String/HTMLElement/Element} container
25607 * container The id or element that will contain the toolbar
25610 * @cfg {Boolean} displayInfo
25611 * True to display the displayMsg (defaults to false)
25614 * @cfg {Number} pageSize
25615 * The number of records to display per page (defaults to 20)
25619 * @cfg {String} displayMsg
25620 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25622 displayMsg : 'Displaying {0} - {1} of {2}',
25624 * @cfg {String} emptyMsg
25625 * The message to display when no records are found (defaults to "No data to display")
25627 emptyMsg : 'No data to display',
25629 * Customizable piece of the default paging text (defaults to "Page")
25632 beforePageText : "Page",
25634 * Customizable piece of the default paging text (defaults to "of %0")
25637 afterPageText : "of {0}",
25639 * Customizable piece of the default paging text (defaults to "First Page")
25642 firstText : "First Page",
25644 * Customizable piece of the default paging text (defaults to "Previous Page")
25647 prevText : "Previous Page",
25649 * Customizable piece of the default paging text (defaults to "Next Page")
25652 nextText : "Next Page",
25654 * Customizable piece of the default paging text (defaults to "Last Page")
25657 lastText : "Last Page",
25659 * Customizable piece of the default paging text (defaults to "Refresh")
25662 refreshText : "Refresh",
25666 onRender : function(ct, position)
25668 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25669 this.navgroup.parentId = this.id;
25670 this.navgroup.onRender(this.el, null);
25671 // add the buttons to the navgroup
25673 if(this.displayInfo){
25674 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25675 this.displayEl = this.el.select('.x-paging-info', true).first();
25676 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25677 // this.displayEl = navel.el.select('span',true).first();
25683 Roo.each(_this.buttons, function(e){ // this might need to use render????
25684 Roo.factory(e).render(_this.el);
25688 Roo.each(_this.toolbarItems, function(e) {
25689 _this.navgroup.addItem(e);
25693 this.first = this.navgroup.addItem({
25694 tooltip: this.firstText,
25695 cls: "prev btn-outline-secondary",
25696 html : ' <i class="fa fa-step-backward"></i>',
25698 preventDefault: true,
25699 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25702 this.prev = this.navgroup.addItem({
25703 tooltip: this.prevText,
25704 cls: "prev btn-outline-secondary",
25705 html : ' <i class="fa fa-backward"></i>',
25707 preventDefault: true,
25708 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25710 //this.addSeparator();
25713 var field = this.navgroup.addItem( {
25715 cls : 'x-paging-position btn-outline-secondary',
25717 html : this.beforePageText +
25718 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25719 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25722 this.field = field.el.select('input', true).first();
25723 this.field.on("keydown", this.onPagingKeydown, this);
25724 this.field.on("focus", function(){this.dom.select();});
25727 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25728 //this.field.setHeight(18);
25729 //this.addSeparator();
25730 this.next = this.navgroup.addItem({
25731 tooltip: this.nextText,
25732 cls: "next btn-outline-secondary",
25733 html : ' <i class="fa fa-forward"></i>',
25735 preventDefault: true,
25736 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25738 this.last = this.navgroup.addItem({
25739 tooltip: this.lastText,
25740 html : ' <i class="fa fa-step-forward"></i>',
25741 cls: "next btn-outline-secondary",
25743 preventDefault: true,
25744 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25746 //this.addSeparator();
25747 this.loading = this.navgroup.addItem({
25748 tooltip: this.refreshText,
25749 cls: "btn-outline-secondary",
25750 html : ' <i class="fa fa-refresh"></i>',
25751 preventDefault: true,
25752 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25758 updateInfo : function(){
25759 if(this.displayEl){
25760 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25761 var msg = count == 0 ?
25765 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25767 this.displayEl.update(msg);
25772 onLoad : function(ds, r, o)
25774 this.cursor = o.params.start ? o.params.start : 0;
25776 var d = this.getPageData(),
25781 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25782 this.field.dom.value = ap;
25783 this.first.setDisabled(ap == 1);
25784 this.prev.setDisabled(ap == 1);
25785 this.next.setDisabled(ap == ps);
25786 this.last.setDisabled(ap == ps);
25787 this.loading.enable();
25792 getPageData : function(){
25793 var total = this.ds.getTotalCount();
25796 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25797 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25802 onLoadError : function(){
25803 this.loading.enable();
25807 onPagingKeydown : function(e){
25808 var k = e.getKey();
25809 var d = this.getPageData();
25811 var v = this.field.dom.value, pageNum;
25812 if(!v || isNaN(pageNum = parseInt(v, 10))){
25813 this.field.dom.value = d.activePage;
25816 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25817 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25820 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))
25822 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25823 this.field.dom.value = pageNum;
25824 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25827 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25829 var v = this.field.dom.value, pageNum;
25830 var increment = (e.shiftKey) ? 10 : 1;
25831 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25834 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25835 this.field.dom.value = d.activePage;
25838 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25840 this.field.dom.value = parseInt(v, 10) + increment;
25841 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25842 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25849 beforeLoad : function(){
25851 this.loading.disable();
25856 onClick : function(which){
25865 ds.load({params:{start: 0, limit: this.pageSize}});
25868 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25871 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25874 var total = ds.getTotalCount();
25875 var extra = total % this.pageSize;
25876 var lastStart = extra ? (total - extra) : total-this.pageSize;
25877 ds.load({params:{start: lastStart, limit: this.pageSize}});
25880 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25886 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25887 * @param {Roo.data.Store} store The data store to unbind
25889 unbind : function(ds){
25890 ds.un("beforeload", this.beforeLoad, this);
25891 ds.un("load", this.onLoad, this);
25892 ds.un("loadexception", this.onLoadError, this);
25893 ds.un("remove", this.updateInfo, this);
25894 ds.un("add", this.updateInfo, this);
25895 this.ds = undefined;
25899 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25900 * @param {Roo.data.Store} store The data store to bind
25902 bind : function(ds){
25903 ds.on("beforeload", this.beforeLoad, this);
25904 ds.on("load", this.onLoad, this);
25905 ds.on("loadexception", this.onLoadError, this);
25906 ds.on("remove", this.updateInfo, this);
25907 ds.on("add", this.updateInfo, this);
25918 * @class Roo.bootstrap.MessageBar
25919 * @extends Roo.bootstrap.Component
25920 * Bootstrap MessageBar class
25921 * @cfg {String} html contents of the MessageBar
25922 * @cfg {String} weight (info | success | warning | danger) default info
25923 * @cfg {String} beforeClass insert the bar before the given class
25924 * @cfg {Boolean} closable (true | false) default false
25925 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25928 * Create a new Element
25929 * @param {Object} config The config object
25932 Roo.bootstrap.MessageBar = function(config){
25933 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25936 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25942 beforeClass: 'bootstrap-sticky-wrap',
25944 getAutoCreate : function(){
25948 cls: 'alert alert-dismissable alert-' + this.weight,
25953 html: this.html || ''
25959 cfg.cls += ' alert-messages-fixed';
25973 onRender : function(ct, position)
25975 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25978 var cfg = Roo.apply({}, this.getAutoCreate());
25982 cfg.cls += ' ' + this.cls;
25985 cfg.style = this.style;
25987 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25989 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25992 this.el.select('>button.close').on('click', this.hide, this);
25998 if (!this.rendered) {
26004 this.fireEvent('show', this);
26010 if (!this.rendered) {
26016 this.fireEvent('hide', this);
26019 update : function()
26021 // var e = this.el.dom.firstChild;
26023 // if(this.closable){
26024 // e = e.nextSibling;
26027 // e.data = this.html || '';
26029 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26045 * @class Roo.bootstrap.Graph
26046 * @extends Roo.bootstrap.Component
26047 * Bootstrap Graph class
26051 @cfg {String} graphtype bar | vbar | pie
26052 @cfg {number} g_x coodinator | centre x (pie)
26053 @cfg {number} g_y coodinator | centre y (pie)
26054 @cfg {number} g_r radius (pie)
26055 @cfg {number} g_height height of the chart (respected by all elements in the set)
26056 @cfg {number} g_width width of the chart (respected by all elements in the set)
26057 @cfg {Object} title The title of the chart
26060 -opts (object) options for the chart
26062 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26063 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26065 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.
26066 o stacked (boolean) whether or not to tread values as in a stacked bar chart
26068 o stretch (boolean)
26070 -opts (object) options for the pie
26073 o startAngle (number)
26074 o endAngle (number)
26078 * Create a new Input
26079 * @param {Object} config The config object
26082 Roo.bootstrap.Graph = function(config){
26083 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26089 * The img click event for the img.
26090 * @param {Roo.EventObject} e
26096 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
26107 //g_colors: this.colors,
26114 getAutoCreate : function(){
26125 onRender : function(ct,position){
26128 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26130 if (typeof(Raphael) == 'undefined') {
26131 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26135 this.raphael = Raphael(this.el.dom);
26137 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26138 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26139 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26140 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26142 r.text(160, 10, "Single Series Chart").attr(txtattr);
26143 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26144 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26145 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26147 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26148 r.barchart(330, 10, 300, 220, data1);
26149 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26150 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26153 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26154 // r.barchart(30, 30, 560, 250, xdata, {
26155 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26156 // axis : "0 0 1 1",
26157 // axisxlabels : xdata
26158 // //yvalues : cols,
26161 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26163 // this.load(null,xdata,{
26164 // axis : "0 0 1 1",
26165 // axisxlabels : xdata
26170 load : function(graphtype,xdata,opts)
26172 this.raphael.clear();
26174 graphtype = this.graphtype;
26179 var r = this.raphael,
26180 fin = function () {
26181 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26183 fout = function () {
26184 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26186 pfin = function() {
26187 this.sector.stop();
26188 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26191 this.label[0].stop();
26192 this.label[0].attr({ r: 7.5 });
26193 this.label[1].attr({ "font-weight": 800 });
26196 pfout = function() {
26197 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26200 this.label[0].animate({ r: 5 }, 500, "bounce");
26201 this.label[1].attr({ "font-weight": 400 });
26207 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26210 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26213 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26214 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26216 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26223 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26228 setTitle: function(o)
26233 initEvents: function() {
26236 this.el.on('click', this.onClick, this);
26240 onClick : function(e)
26242 Roo.log('img onclick');
26243 this.fireEvent('click', this, e);
26255 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26258 * @class Roo.bootstrap.dash.NumberBox
26259 * @extends Roo.bootstrap.Component
26260 * Bootstrap NumberBox class
26261 * @cfg {String} headline Box headline
26262 * @cfg {String} content Box content
26263 * @cfg {String} icon Box icon
26264 * @cfg {String} footer Footer text
26265 * @cfg {String} fhref Footer href
26268 * Create a new NumberBox
26269 * @param {Object} config The config object
26273 Roo.bootstrap.dash.NumberBox = function(config){
26274 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26278 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26287 getAutoCreate : function(){
26291 cls : 'small-box ',
26299 cls : 'roo-headline',
26300 html : this.headline
26304 cls : 'roo-content',
26305 html : this.content
26319 cls : 'ion ' + this.icon
26328 cls : 'small-box-footer',
26329 href : this.fhref || '#',
26333 cfg.cn.push(footer);
26340 onRender : function(ct,position){
26341 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26348 setHeadline: function (value)
26350 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26353 setFooter: function (value, href)
26355 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26358 this.el.select('a.small-box-footer',true).first().attr('href', href);
26363 setContent: function (value)
26365 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26368 initEvents: function()
26382 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26385 * @class Roo.bootstrap.dash.TabBox
26386 * @extends Roo.bootstrap.Component
26387 * Bootstrap TabBox class
26388 * @cfg {String} title Title of the TabBox
26389 * @cfg {String} icon Icon of the TabBox
26390 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26391 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26394 * Create a new TabBox
26395 * @param {Object} config The config object
26399 Roo.bootstrap.dash.TabBox = function(config){
26400 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26405 * When a pane is added
26406 * @param {Roo.bootstrap.dash.TabPane} pane
26410 * @event activatepane
26411 * When a pane is activated
26412 * @param {Roo.bootstrap.dash.TabPane} pane
26414 "activatepane" : true
26422 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26427 tabScrollable : false,
26429 getChildContainer : function()
26431 return this.el.select('.tab-content', true).first();
26434 getAutoCreate : function(){
26438 cls: 'pull-left header',
26446 cls: 'fa ' + this.icon
26452 cls: 'nav nav-tabs pull-right',
26458 if(this.tabScrollable){
26465 cls: 'nav nav-tabs pull-right',
26476 cls: 'nav-tabs-custom',
26481 cls: 'tab-content no-padding',
26489 initEvents : function()
26491 //Roo.log('add add pane handler');
26492 this.on('addpane', this.onAddPane, this);
26495 * Updates the box title
26496 * @param {String} html to set the title to.
26498 setTitle : function(value)
26500 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26502 onAddPane : function(pane)
26504 this.panes.push(pane);
26505 //Roo.log('addpane');
26507 // tabs are rendere left to right..
26508 if(!this.showtabs){
26512 var ctr = this.el.select('.nav-tabs', true).first();
26515 var existing = ctr.select('.nav-tab',true);
26516 var qty = existing.getCount();;
26519 var tab = ctr.createChild({
26521 cls : 'nav-tab' + (qty ? '' : ' active'),
26529 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26532 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26534 pane.el.addClass('active');
26539 onTabClick : function(ev,un,ob,pane)
26541 //Roo.log('tab - prev default');
26542 ev.preventDefault();
26545 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26546 pane.tab.addClass('active');
26547 //Roo.log(pane.title);
26548 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26549 // technically we should have a deactivate event.. but maybe add later.
26550 // and it should not de-activate the selected tab...
26551 this.fireEvent('activatepane', pane);
26552 pane.el.addClass('active');
26553 pane.fireEvent('activate');
26558 getActivePane : function()
26561 Roo.each(this.panes, function(p) {
26562 if(p.el.hasClass('active')){
26583 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26585 * @class Roo.bootstrap.TabPane
26586 * @extends Roo.bootstrap.Component
26587 * Bootstrap TabPane class
26588 * @cfg {Boolean} active (false | true) Default false
26589 * @cfg {String} title title of panel
26593 * Create a new TabPane
26594 * @param {Object} config The config object
26597 Roo.bootstrap.dash.TabPane = function(config){
26598 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26604 * When a pane is activated
26605 * @param {Roo.bootstrap.dash.TabPane} pane
26612 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26617 // the tabBox that this is attached to.
26620 getAutoCreate : function()
26628 cfg.cls += ' active';
26633 initEvents : function()
26635 //Roo.log('trigger add pane handler');
26636 this.parent().fireEvent('addpane', this)
26640 * Updates the tab title
26641 * @param {String} html to set the title to.
26643 setTitle: function(str)
26649 this.tab.select('a', true).first().dom.innerHTML = str;
26666 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26669 * @class Roo.bootstrap.menu.Menu
26670 * @extends Roo.bootstrap.Component
26671 * Bootstrap Menu class - container for Menu
26672 * @cfg {String} html Text of the menu
26673 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26674 * @cfg {String} icon Font awesome icon
26675 * @cfg {String} pos Menu align to (top | bottom) default bottom
26679 * Create a new Menu
26680 * @param {Object} config The config object
26684 Roo.bootstrap.menu.Menu = function(config){
26685 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26689 * @event beforeshow
26690 * Fires before this menu is displayed
26691 * @param {Roo.bootstrap.menu.Menu} this
26695 * @event beforehide
26696 * Fires before this menu is hidden
26697 * @param {Roo.bootstrap.menu.Menu} this
26702 * Fires after this menu is displayed
26703 * @param {Roo.bootstrap.menu.Menu} this
26708 * Fires after this menu is hidden
26709 * @param {Roo.bootstrap.menu.Menu} this
26714 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26715 * @param {Roo.bootstrap.menu.Menu} this
26716 * @param {Roo.EventObject} e
26723 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26727 weight : 'default',
26732 getChildContainer : function() {
26733 if(this.isSubMenu){
26737 return this.el.select('ul.dropdown-menu', true).first();
26740 getAutoCreate : function()
26745 cls : 'roo-menu-text',
26753 cls : 'fa ' + this.icon
26764 cls : 'dropdown-button btn btn-' + this.weight,
26769 cls : 'dropdown-toggle btn btn-' + this.weight,
26779 cls : 'dropdown-menu'
26785 if(this.pos == 'top'){
26786 cfg.cls += ' dropup';
26789 if(this.isSubMenu){
26792 cls : 'dropdown-menu'
26799 onRender : function(ct, position)
26801 this.isSubMenu = ct.hasClass('dropdown-submenu');
26803 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26806 initEvents : function()
26808 if(this.isSubMenu){
26812 this.hidden = true;
26814 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26815 this.triggerEl.on('click', this.onTriggerPress, this);
26817 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26818 this.buttonEl.on('click', this.onClick, this);
26824 if(this.isSubMenu){
26828 return this.el.select('ul.dropdown-menu', true).first();
26831 onClick : function(e)
26833 this.fireEvent("click", this, e);
26836 onTriggerPress : function(e)
26838 if (this.isVisible()) {
26845 isVisible : function(){
26846 return !this.hidden;
26851 this.fireEvent("beforeshow", this);
26853 this.hidden = false;
26854 this.el.addClass('open');
26856 Roo.get(document).on("mouseup", this.onMouseUp, this);
26858 this.fireEvent("show", this);
26865 this.fireEvent("beforehide", this);
26867 this.hidden = true;
26868 this.el.removeClass('open');
26870 Roo.get(document).un("mouseup", this.onMouseUp);
26872 this.fireEvent("hide", this);
26875 onMouseUp : function()
26889 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26892 * @class Roo.bootstrap.menu.Item
26893 * @extends Roo.bootstrap.Component
26894 * Bootstrap MenuItem class
26895 * @cfg {Boolean} submenu (true | false) default false
26896 * @cfg {String} html text of the item
26897 * @cfg {String} href the link
26898 * @cfg {Boolean} disable (true | false) default false
26899 * @cfg {Boolean} preventDefault (true | false) default true
26900 * @cfg {String} icon Font awesome icon
26901 * @cfg {String} pos Submenu align to (left | right) default right
26905 * Create a new Item
26906 * @param {Object} config The config object
26910 Roo.bootstrap.menu.Item = function(config){
26911 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26915 * Fires when the mouse is hovering over this menu
26916 * @param {Roo.bootstrap.menu.Item} this
26917 * @param {Roo.EventObject} e
26922 * Fires when the mouse exits this menu
26923 * @param {Roo.bootstrap.menu.Item} this
26924 * @param {Roo.EventObject} e
26930 * The raw click event for the entire grid.
26931 * @param {Roo.EventObject} e
26937 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26942 preventDefault: true,
26947 getAutoCreate : function()
26952 cls : 'roo-menu-item-text',
26960 cls : 'fa ' + this.icon
26969 href : this.href || '#',
26976 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26980 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26982 if(this.pos == 'left'){
26983 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26990 initEvents : function()
26992 this.el.on('mouseover', this.onMouseOver, this);
26993 this.el.on('mouseout', this.onMouseOut, this);
26995 this.el.select('a', true).first().on('click', this.onClick, this);
26999 onClick : function(e)
27001 if(this.preventDefault){
27002 e.preventDefault();
27005 this.fireEvent("click", this, e);
27008 onMouseOver : function(e)
27010 if(this.submenu && this.pos == 'left'){
27011 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27014 this.fireEvent("mouseover", this, e);
27017 onMouseOut : function(e)
27019 this.fireEvent("mouseout", this, e);
27031 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27034 * @class Roo.bootstrap.menu.Separator
27035 * @extends Roo.bootstrap.Component
27036 * Bootstrap Separator class
27039 * Create a new Separator
27040 * @param {Object} config The config object
27044 Roo.bootstrap.menu.Separator = function(config){
27045 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27048 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
27050 getAutoCreate : function(){
27071 * @class Roo.bootstrap.Tooltip
27072 * Bootstrap Tooltip class
27073 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27074 * to determine which dom element triggers the tooltip.
27076 * It needs to add support for additional attributes like tooltip-position
27079 * Create a new Toolti
27080 * @param {Object} config The config object
27083 Roo.bootstrap.Tooltip = function(config){
27084 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27086 this.alignment = Roo.bootstrap.Tooltip.alignment;
27088 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27089 this.alignment = config.alignment;
27094 Roo.apply(Roo.bootstrap.Tooltip, {
27096 * @function init initialize tooltip monitoring.
27100 currentTip : false,
27101 currentRegion : false,
27107 Roo.get(document).on('mouseover', this.enter ,this);
27108 Roo.get(document).on('mouseout', this.leave, this);
27111 this.currentTip = new Roo.bootstrap.Tooltip();
27114 enter : function(ev)
27116 var dom = ev.getTarget();
27118 //Roo.log(['enter',dom]);
27119 var el = Roo.fly(dom);
27120 if (this.currentEl) {
27122 //Roo.log(this.currentEl);
27123 //Roo.log(this.currentEl.contains(dom));
27124 if (this.currentEl == el) {
27127 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27133 if (this.currentTip.el) {
27134 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27138 if(!el || el.dom == document){
27144 // you can not look for children, as if el is the body.. then everythign is the child..
27145 if (!el.attr('tooltip')) { //
27146 if (!el.select("[tooltip]").elements.length) {
27149 // is the mouse over this child...?
27150 bindEl = el.select("[tooltip]").first();
27151 var xy = ev.getXY();
27152 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27153 //Roo.log("not in region.");
27156 //Roo.log("child element over..");
27159 this.currentEl = bindEl;
27160 this.currentTip.bind(bindEl);
27161 this.currentRegion = Roo.lib.Region.getRegion(dom);
27162 this.currentTip.enter();
27165 leave : function(ev)
27167 var dom = ev.getTarget();
27168 //Roo.log(['leave',dom]);
27169 if (!this.currentEl) {
27174 if (dom != this.currentEl.dom) {
27177 var xy = ev.getXY();
27178 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
27181 // only activate leave if mouse cursor is outside... bounding box..
27186 if (this.currentTip) {
27187 this.currentTip.leave();
27189 //Roo.log('clear currentEl');
27190 this.currentEl = false;
27195 'left' : ['r-l', [-2,0], 'right'],
27196 'right' : ['l-r', [2,0], 'left'],
27197 'bottom' : ['t-b', [0,2], 'top'],
27198 'top' : [ 'b-t', [0,-2], 'bottom']
27204 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27209 delay : null, // can be { show : 300 , hide: 500}
27213 hoverState : null, //???
27215 placement : 'bottom',
27219 getAutoCreate : function(){
27226 cls : 'tooltip-arrow'
27229 cls : 'tooltip-inner'
27236 bind : function(el)
27242 enter : function () {
27244 if (this.timeout != null) {
27245 clearTimeout(this.timeout);
27248 this.hoverState = 'in';
27249 //Roo.log("enter - show");
27250 if (!this.delay || !this.delay.show) {
27255 this.timeout = setTimeout(function () {
27256 if (_t.hoverState == 'in') {
27259 }, this.delay.show);
27263 clearTimeout(this.timeout);
27265 this.hoverState = 'out';
27266 if (!this.delay || !this.delay.hide) {
27272 this.timeout = setTimeout(function () {
27273 //Roo.log("leave - timeout");
27275 if (_t.hoverState == 'out') {
27277 Roo.bootstrap.Tooltip.currentEl = false;
27282 show : function (msg)
27285 this.render(document.body);
27288 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27290 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27292 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27294 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27296 var placement = typeof this.placement == 'function' ?
27297 this.placement.call(this, this.el, on_el) :
27300 var autoToken = /\s?auto?\s?/i;
27301 var autoPlace = autoToken.test(placement);
27303 placement = placement.replace(autoToken, '') || 'top';
27307 //this.el.setXY([0,0]);
27309 //this.el.dom.style.display='block';
27311 //this.el.appendTo(on_el);
27313 var p = this.getPosition();
27314 var box = this.el.getBox();
27320 var align = this.alignment[placement];
27322 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27324 if(placement == 'top' || placement == 'bottom'){
27326 placement = 'right';
27329 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27330 placement = 'left';
27333 var scroll = Roo.select('body', true).first().getScroll();
27335 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27339 align = this.alignment[placement];
27342 this.el.alignTo(this.bindEl, align[0],align[1]);
27343 //var arrow = this.el.select('.arrow',true).first();
27344 //arrow.set(align[2],
27346 this.el.addClass(placement);
27348 this.el.addClass('in fade');
27350 this.hoverState = null;
27352 if (this.el.hasClass('fade')) {
27363 //this.el.setXY([0,0]);
27364 this.el.removeClass('in');
27380 * @class Roo.bootstrap.LocationPicker
27381 * @extends Roo.bootstrap.Component
27382 * Bootstrap LocationPicker class
27383 * @cfg {Number} latitude Position when init default 0
27384 * @cfg {Number} longitude Position when init default 0
27385 * @cfg {Number} zoom default 15
27386 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27387 * @cfg {Boolean} mapTypeControl default false
27388 * @cfg {Boolean} disableDoubleClickZoom default false
27389 * @cfg {Boolean} scrollwheel default true
27390 * @cfg {Boolean} streetViewControl default false
27391 * @cfg {Number} radius default 0
27392 * @cfg {String} locationName
27393 * @cfg {Boolean} draggable default true
27394 * @cfg {Boolean} enableAutocomplete default false
27395 * @cfg {Boolean} enableReverseGeocode default true
27396 * @cfg {String} markerTitle
27399 * Create a new LocationPicker
27400 * @param {Object} config The config object
27404 Roo.bootstrap.LocationPicker = function(config){
27406 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27411 * Fires when the picker initialized.
27412 * @param {Roo.bootstrap.LocationPicker} this
27413 * @param {Google Location} location
27417 * @event positionchanged
27418 * Fires when the picker position changed.
27419 * @param {Roo.bootstrap.LocationPicker} this
27420 * @param {Google Location} location
27422 positionchanged : true,
27425 * Fires when the map resize.
27426 * @param {Roo.bootstrap.LocationPicker} this
27431 * Fires when the map show.
27432 * @param {Roo.bootstrap.LocationPicker} this
27437 * Fires when the map hide.
27438 * @param {Roo.bootstrap.LocationPicker} this
27443 * Fires when click the map.
27444 * @param {Roo.bootstrap.LocationPicker} this
27445 * @param {Map event} e
27449 * @event mapRightClick
27450 * Fires when right click the map.
27451 * @param {Roo.bootstrap.LocationPicker} this
27452 * @param {Map event} e
27454 mapRightClick : true,
27456 * @event markerClick
27457 * Fires when click the marker.
27458 * @param {Roo.bootstrap.LocationPicker} this
27459 * @param {Map event} e
27461 markerClick : true,
27463 * @event markerRightClick
27464 * Fires when right click the marker.
27465 * @param {Roo.bootstrap.LocationPicker} this
27466 * @param {Map event} e
27468 markerRightClick : true,
27470 * @event OverlayViewDraw
27471 * Fires when OverlayView Draw
27472 * @param {Roo.bootstrap.LocationPicker} this
27474 OverlayViewDraw : true,
27476 * @event OverlayViewOnAdd
27477 * Fires when OverlayView Draw
27478 * @param {Roo.bootstrap.LocationPicker} this
27480 OverlayViewOnAdd : true,
27482 * @event OverlayViewOnRemove
27483 * Fires when OverlayView Draw
27484 * @param {Roo.bootstrap.LocationPicker} this
27486 OverlayViewOnRemove : true,
27488 * @event OverlayViewShow
27489 * Fires when OverlayView Draw
27490 * @param {Roo.bootstrap.LocationPicker} this
27491 * @param {Pixel} cpx
27493 OverlayViewShow : true,
27495 * @event OverlayViewHide
27496 * Fires when OverlayView Draw
27497 * @param {Roo.bootstrap.LocationPicker} this
27499 OverlayViewHide : true,
27501 * @event loadexception
27502 * Fires when load google lib failed.
27503 * @param {Roo.bootstrap.LocationPicker} this
27505 loadexception : true
27510 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27512 gMapContext: false,
27518 mapTypeControl: false,
27519 disableDoubleClickZoom: false,
27521 streetViewControl: false,
27525 enableAutocomplete: false,
27526 enableReverseGeocode: true,
27529 getAutoCreate: function()
27534 cls: 'roo-location-picker'
27540 initEvents: function(ct, position)
27542 if(!this.el.getWidth() || this.isApplied()){
27546 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27551 initial: function()
27553 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27554 this.fireEvent('loadexception', this);
27558 if(!this.mapTypeId){
27559 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27562 this.gMapContext = this.GMapContext();
27564 this.initOverlayView();
27566 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27570 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27571 _this.setPosition(_this.gMapContext.marker.position);
27574 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27575 _this.fireEvent('mapClick', this, event);
27579 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27580 _this.fireEvent('mapRightClick', this, event);
27584 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27585 _this.fireEvent('markerClick', this, event);
27589 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27590 _this.fireEvent('markerRightClick', this, event);
27594 this.setPosition(this.gMapContext.location);
27596 this.fireEvent('initial', this, this.gMapContext.location);
27599 initOverlayView: function()
27603 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27607 _this.fireEvent('OverlayViewDraw', _this);
27612 _this.fireEvent('OverlayViewOnAdd', _this);
27615 onRemove: function()
27617 _this.fireEvent('OverlayViewOnRemove', _this);
27620 show: function(cpx)
27622 _this.fireEvent('OverlayViewShow', _this, cpx);
27627 _this.fireEvent('OverlayViewHide', _this);
27633 fromLatLngToContainerPixel: function(event)
27635 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27638 isApplied: function()
27640 return this.getGmapContext() == false ? false : true;
27643 getGmapContext: function()
27645 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27648 GMapContext: function()
27650 var position = new google.maps.LatLng(this.latitude, this.longitude);
27652 var _map = new google.maps.Map(this.el.dom, {
27655 mapTypeId: this.mapTypeId,
27656 mapTypeControl: this.mapTypeControl,
27657 disableDoubleClickZoom: this.disableDoubleClickZoom,
27658 scrollwheel: this.scrollwheel,
27659 streetViewControl: this.streetViewControl,
27660 locationName: this.locationName,
27661 draggable: this.draggable,
27662 enableAutocomplete: this.enableAutocomplete,
27663 enableReverseGeocode: this.enableReverseGeocode
27666 var _marker = new google.maps.Marker({
27667 position: position,
27669 title: this.markerTitle,
27670 draggable: this.draggable
27677 location: position,
27678 radius: this.radius,
27679 locationName: this.locationName,
27680 addressComponents: {
27681 formatted_address: null,
27682 addressLine1: null,
27683 addressLine2: null,
27685 streetNumber: null,
27689 stateOrProvince: null
27692 domContainer: this.el.dom,
27693 geodecoder: new google.maps.Geocoder()
27697 drawCircle: function(center, radius, options)
27699 if (this.gMapContext.circle != null) {
27700 this.gMapContext.circle.setMap(null);
27704 options = Roo.apply({}, options, {
27705 strokeColor: "#0000FF",
27706 strokeOpacity: .35,
27708 fillColor: "#0000FF",
27712 options.map = this.gMapContext.map;
27713 options.radius = radius;
27714 options.center = center;
27715 this.gMapContext.circle = new google.maps.Circle(options);
27716 return this.gMapContext.circle;
27722 setPosition: function(location)
27724 this.gMapContext.location = location;
27725 this.gMapContext.marker.setPosition(location);
27726 this.gMapContext.map.panTo(location);
27727 this.drawCircle(location, this.gMapContext.radius, {});
27731 if (this.gMapContext.settings.enableReverseGeocode) {
27732 this.gMapContext.geodecoder.geocode({
27733 latLng: this.gMapContext.location
27734 }, function(results, status) {
27736 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27737 _this.gMapContext.locationName = results[0].formatted_address;
27738 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27740 _this.fireEvent('positionchanged', this, location);
27747 this.fireEvent('positionchanged', this, location);
27752 google.maps.event.trigger(this.gMapContext.map, "resize");
27754 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27756 this.fireEvent('resize', this);
27759 setPositionByLatLng: function(latitude, longitude)
27761 this.setPosition(new google.maps.LatLng(latitude, longitude));
27764 getCurrentPosition: function()
27767 latitude: this.gMapContext.location.lat(),
27768 longitude: this.gMapContext.location.lng()
27772 getAddressName: function()
27774 return this.gMapContext.locationName;
27777 getAddressComponents: function()
27779 return this.gMapContext.addressComponents;
27782 address_component_from_google_geocode: function(address_components)
27786 for (var i = 0; i < address_components.length; i++) {
27787 var component = address_components[i];
27788 if (component.types.indexOf("postal_code") >= 0) {
27789 result.postalCode = component.short_name;
27790 } else if (component.types.indexOf("street_number") >= 0) {
27791 result.streetNumber = component.short_name;
27792 } else if (component.types.indexOf("route") >= 0) {
27793 result.streetName = component.short_name;
27794 } else if (component.types.indexOf("neighborhood") >= 0) {
27795 result.city = component.short_name;
27796 } else if (component.types.indexOf("locality") >= 0) {
27797 result.city = component.short_name;
27798 } else if (component.types.indexOf("sublocality") >= 0) {
27799 result.district = component.short_name;
27800 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27801 result.stateOrProvince = component.short_name;
27802 } else if (component.types.indexOf("country") >= 0) {
27803 result.country = component.short_name;
27807 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27808 result.addressLine2 = "";
27812 setZoomLevel: function(zoom)
27814 this.gMapContext.map.setZoom(zoom);
27827 this.fireEvent('show', this);
27838 this.fireEvent('hide', this);
27843 Roo.apply(Roo.bootstrap.LocationPicker, {
27845 OverlayView : function(map, options)
27847 options = options || {};
27854 * @class Roo.bootstrap.Alert
27855 * @extends Roo.bootstrap.Component
27856 * Bootstrap Alert class - shows an alert area box
27858 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27859 Enter a valid email address
27862 * @cfg {String} title The title of alert
27863 * @cfg {String} html The content of alert
27864 * @cfg {String} weight ( success | info | warning | danger )
27865 * @cfg {String} faicon font-awesomeicon
27868 * Create a new alert
27869 * @param {Object} config The config object
27873 Roo.bootstrap.Alert = function(config){
27874 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27878 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27885 getAutoCreate : function()
27894 cls : 'roo-alert-icon'
27899 cls : 'roo-alert-title',
27904 cls : 'roo-alert-text',
27911 cfg.cn[0].cls += ' fa ' + this.faicon;
27915 cfg.cls += ' alert-' + this.weight;
27921 initEvents: function()
27923 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27926 setTitle : function(str)
27928 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27931 setText : function(str)
27933 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27936 setWeight : function(weight)
27939 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27942 this.weight = weight;
27944 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27947 setIcon : function(icon)
27950 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27953 this.faicon = icon;
27955 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27976 * @class Roo.bootstrap.UploadCropbox
27977 * @extends Roo.bootstrap.Component
27978 * Bootstrap UploadCropbox class
27979 * @cfg {String} emptyText show when image has been loaded
27980 * @cfg {String} rotateNotify show when image too small to rotate
27981 * @cfg {Number} errorTimeout default 3000
27982 * @cfg {Number} minWidth default 300
27983 * @cfg {Number} minHeight default 300
27984 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27985 * @cfg {Boolean} isDocument (true|false) default false
27986 * @cfg {String} url action url
27987 * @cfg {String} paramName default 'imageUpload'
27988 * @cfg {String} method default POST
27989 * @cfg {Boolean} loadMask (true|false) default true
27990 * @cfg {Boolean} loadingText default 'Loading...'
27993 * Create a new UploadCropbox
27994 * @param {Object} config The config object
27997 Roo.bootstrap.UploadCropbox = function(config){
27998 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28002 * @event beforeselectfile
28003 * Fire before select file
28004 * @param {Roo.bootstrap.UploadCropbox} this
28006 "beforeselectfile" : true,
28009 * Fire after initEvent
28010 * @param {Roo.bootstrap.UploadCropbox} this
28015 * Fire after initEvent
28016 * @param {Roo.bootstrap.UploadCropbox} this
28017 * @param {String} data
28022 * Fire when preparing the file data
28023 * @param {Roo.bootstrap.UploadCropbox} this
28024 * @param {Object} file
28029 * Fire when get exception
28030 * @param {Roo.bootstrap.UploadCropbox} this
28031 * @param {XMLHttpRequest} xhr
28033 "exception" : true,
28035 * @event beforeloadcanvas
28036 * Fire before load the canvas
28037 * @param {Roo.bootstrap.UploadCropbox} this
28038 * @param {String} src
28040 "beforeloadcanvas" : true,
28043 * Fire when trash image
28044 * @param {Roo.bootstrap.UploadCropbox} this
28049 * Fire when download the image
28050 * @param {Roo.bootstrap.UploadCropbox} this
28054 * @event footerbuttonclick
28055 * Fire when footerbuttonclick
28056 * @param {Roo.bootstrap.UploadCropbox} this
28057 * @param {String} type
28059 "footerbuttonclick" : true,
28063 * @param {Roo.bootstrap.UploadCropbox} this
28068 * Fire when rotate the image
28069 * @param {Roo.bootstrap.UploadCropbox} this
28070 * @param {String} pos
28075 * Fire when inspect the file
28076 * @param {Roo.bootstrap.UploadCropbox} this
28077 * @param {Object} file
28082 * Fire when xhr upload the file
28083 * @param {Roo.bootstrap.UploadCropbox} this
28084 * @param {Object} data
28089 * Fire when arrange the file data
28090 * @param {Roo.bootstrap.UploadCropbox} this
28091 * @param {Object} formData
28096 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28099 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
28101 emptyText : 'Click to upload image',
28102 rotateNotify : 'Image is too small to rotate',
28103 errorTimeout : 3000,
28117 cropType : 'image/jpeg',
28119 canvasLoaded : false,
28120 isDocument : false,
28122 paramName : 'imageUpload',
28124 loadingText : 'Loading...',
28127 getAutoCreate : function()
28131 cls : 'roo-upload-cropbox',
28135 cls : 'roo-upload-cropbox-selector',
28140 cls : 'roo-upload-cropbox-body',
28141 style : 'cursor:pointer',
28145 cls : 'roo-upload-cropbox-preview'
28149 cls : 'roo-upload-cropbox-thumb'
28153 cls : 'roo-upload-cropbox-empty-notify',
28154 html : this.emptyText
28158 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28159 html : this.rotateNotify
28165 cls : 'roo-upload-cropbox-footer',
28168 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28178 onRender : function(ct, position)
28180 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28182 if (this.buttons.length) {
28184 Roo.each(this.buttons, function(bb) {
28186 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28188 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28194 this.maskEl = this.el;
28198 initEvents : function()
28200 this.urlAPI = (window.createObjectURL && window) ||
28201 (window.URL && URL.revokeObjectURL && URL) ||
28202 (window.webkitURL && webkitURL);
28204 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28205 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28207 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28208 this.selectorEl.hide();
28210 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28211 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28213 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28214 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28215 this.thumbEl.hide();
28217 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28218 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28220 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28221 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28222 this.errorEl.hide();
28224 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28225 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28226 this.footerEl.hide();
28228 this.setThumbBoxSize();
28234 this.fireEvent('initial', this);
28241 window.addEventListener("resize", function() { _this.resize(); } );
28243 this.bodyEl.on('click', this.beforeSelectFile, this);
28246 this.bodyEl.on('touchstart', this.onTouchStart, this);
28247 this.bodyEl.on('touchmove', this.onTouchMove, this);
28248 this.bodyEl.on('touchend', this.onTouchEnd, this);
28252 this.bodyEl.on('mousedown', this.onMouseDown, this);
28253 this.bodyEl.on('mousemove', this.onMouseMove, this);
28254 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28255 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28256 Roo.get(document).on('mouseup', this.onMouseUp, this);
28259 this.selectorEl.on('change', this.onFileSelected, this);
28265 this.baseScale = 1;
28267 this.baseRotate = 1;
28268 this.dragable = false;
28269 this.pinching = false;
28272 this.cropData = false;
28273 this.notifyEl.dom.innerHTML = this.emptyText;
28275 this.selectorEl.dom.value = '';
28279 resize : function()
28281 if(this.fireEvent('resize', this) != false){
28282 this.setThumbBoxPosition();
28283 this.setCanvasPosition();
28287 onFooterButtonClick : function(e, el, o, type)
28290 case 'rotate-left' :
28291 this.onRotateLeft(e);
28293 case 'rotate-right' :
28294 this.onRotateRight(e);
28297 this.beforeSelectFile(e);
28312 this.fireEvent('footerbuttonclick', this, type);
28315 beforeSelectFile : function(e)
28317 e.preventDefault();
28319 if(this.fireEvent('beforeselectfile', this) != false){
28320 this.selectorEl.dom.click();
28324 onFileSelected : function(e)
28326 e.preventDefault();
28328 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28332 var file = this.selectorEl.dom.files[0];
28334 if(this.fireEvent('inspect', this, file) != false){
28335 this.prepare(file);
28340 trash : function(e)
28342 this.fireEvent('trash', this);
28345 download : function(e)
28347 this.fireEvent('download', this);
28350 loadCanvas : function(src)
28352 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28356 this.imageEl = document.createElement('img');
28360 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28362 this.imageEl.src = src;
28366 onLoadCanvas : function()
28368 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28369 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28371 this.bodyEl.un('click', this.beforeSelectFile, this);
28373 this.notifyEl.hide();
28374 this.thumbEl.show();
28375 this.footerEl.show();
28377 this.baseRotateLevel();
28379 if(this.isDocument){
28380 this.setThumbBoxSize();
28383 this.setThumbBoxPosition();
28385 this.baseScaleLevel();
28391 this.canvasLoaded = true;
28394 this.maskEl.unmask();
28399 setCanvasPosition : function()
28401 if(!this.canvasEl){
28405 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28406 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28408 this.previewEl.setLeft(pw);
28409 this.previewEl.setTop(ph);
28413 onMouseDown : function(e)
28417 this.dragable = true;
28418 this.pinching = false;
28420 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28421 this.dragable = false;
28425 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28426 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28430 onMouseMove : function(e)
28434 if(!this.canvasLoaded){
28438 if (!this.dragable){
28442 var minX = Math.ceil(this.thumbEl.getLeft(true));
28443 var minY = Math.ceil(this.thumbEl.getTop(true));
28445 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28446 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28448 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28449 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28451 x = x - this.mouseX;
28452 y = y - this.mouseY;
28454 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28455 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28457 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28458 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28460 this.previewEl.setLeft(bgX);
28461 this.previewEl.setTop(bgY);
28463 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28464 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28467 onMouseUp : function(e)
28471 this.dragable = false;
28474 onMouseWheel : function(e)
28478 this.startScale = this.scale;
28480 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28482 if(!this.zoomable()){
28483 this.scale = this.startScale;
28492 zoomable : function()
28494 var minScale = this.thumbEl.getWidth() / this.minWidth;
28496 if(this.minWidth < this.minHeight){
28497 minScale = this.thumbEl.getHeight() / this.minHeight;
28500 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28501 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28505 (this.rotate == 0 || this.rotate == 180) &&
28507 width > this.imageEl.OriginWidth ||
28508 height > this.imageEl.OriginHeight ||
28509 (width < this.minWidth && height < this.minHeight)
28517 (this.rotate == 90 || this.rotate == 270) &&
28519 width > this.imageEl.OriginWidth ||
28520 height > this.imageEl.OriginHeight ||
28521 (width < this.minHeight && height < this.minWidth)
28528 !this.isDocument &&
28529 (this.rotate == 0 || this.rotate == 180) &&
28531 width < this.minWidth ||
28532 width > this.imageEl.OriginWidth ||
28533 height < this.minHeight ||
28534 height > this.imageEl.OriginHeight
28541 !this.isDocument &&
28542 (this.rotate == 90 || this.rotate == 270) &&
28544 width < this.minHeight ||
28545 width > this.imageEl.OriginWidth ||
28546 height < this.minWidth ||
28547 height > this.imageEl.OriginHeight
28557 onRotateLeft : function(e)
28559 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28561 var minScale = this.thumbEl.getWidth() / this.minWidth;
28563 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28564 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28566 this.startScale = this.scale;
28568 while (this.getScaleLevel() < minScale){
28570 this.scale = this.scale + 1;
28572 if(!this.zoomable()){
28577 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28578 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28583 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28590 this.scale = this.startScale;
28592 this.onRotateFail();
28597 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28599 if(this.isDocument){
28600 this.setThumbBoxSize();
28601 this.setThumbBoxPosition();
28602 this.setCanvasPosition();
28607 this.fireEvent('rotate', this, 'left');
28611 onRotateRight : function(e)
28613 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28615 var minScale = this.thumbEl.getWidth() / this.minWidth;
28617 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28618 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28620 this.startScale = this.scale;
28622 while (this.getScaleLevel() < minScale){
28624 this.scale = this.scale + 1;
28626 if(!this.zoomable()){
28631 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28632 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28637 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28644 this.scale = this.startScale;
28646 this.onRotateFail();
28651 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28653 if(this.isDocument){
28654 this.setThumbBoxSize();
28655 this.setThumbBoxPosition();
28656 this.setCanvasPosition();
28661 this.fireEvent('rotate', this, 'right');
28664 onRotateFail : function()
28666 this.errorEl.show(true);
28670 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28675 this.previewEl.dom.innerHTML = '';
28677 var canvasEl = document.createElement("canvas");
28679 var contextEl = canvasEl.getContext("2d");
28681 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28682 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28683 var center = this.imageEl.OriginWidth / 2;
28685 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28686 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28687 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28688 center = this.imageEl.OriginHeight / 2;
28691 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28693 contextEl.translate(center, center);
28694 contextEl.rotate(this.rotate * Math.PI / 180);
28696 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28698 this.canvasEl = document.createElement("canvas");
28700 this.contextEl = this.canvasEl.getContext("2d");
28702 switch (this.rotate) {
28705 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28706 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28708 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28713 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28714 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28716 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28717 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);
28721 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28726 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28727 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28729 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28730 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);
28734 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);
28739 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28740 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28742 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28743 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28747 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);
28754 this.previewEl.appendChild(this.canvasEl);
28756 this.setCanvasPosition();
28761 if(!this.canvasLoaded){
28765 var imageCanvas = document.createElement("canvas");
28767 var imageContext = imageCanvas.getContext("2d");
28769 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28770 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28772 var center = imageCanvas.width / 2;
28774 imageContext.translate(center, center);
28776 imageContext.rotate(this.rotate * Math.PI / 180);
28778 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28780 var canvas = document.createElement("canvas");
28782 var context = canvas.getContext("2d");
28784 canvas.width = this.minWidth;
28785 canvas.height = this.minHeight;
28787 switch (this.rotate) {
28790 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28791 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28793 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28794 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28796 var targetWidth = this.minWidth - 2 * x;
28797 var targetHeight = this.minHeight - 2 * y;
28801 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28802 scale = targetWidth / width;
28805 if(x > 0 && y == 0){
28806 scale = targetHeight / height;
28809 if(x > 0 && y > 0){
28810 scale = targetWidth / width;
28812 if(width < height){
28813 scale = targetHeight / height;
28817 context.scale(scale, scale);
28819 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28820 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28822 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28823 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28825 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28830 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28831 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28833 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28834 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28836 var targetWidth = this.minWidth - 2 * x;
28837 var targetHeight = this.minHeight - 2 * y;
28841 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28842 scale = targetWidth / width;
28845 if(x > 0 && y == 0){
28846 scale = targetHeight / height;
28849 if(x > 0 && y > 0){
28850 scale = targetWidth / width;
28852 if(width < height){
28853 scale = targetHeight / height;
28857 context.scale(scale, scale);
28859 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28860 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28862 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28863 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28865 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28867 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28872 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28873 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28875 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28876 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28878 var targetWidth = this.minWidth - 2 * x;
28879 var targetHeight = this.minHeight - 2 * y;
28883 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28884 scale = targetWidth / width;
28887 if(x > 0 && y == 0){
28888 scale = targetHeight / height;
28891 if(x > 0 && y > 0){
28892 scale = targetWidth / width;
28894 if(width < height){
28895 scale = targetHeight / height;
28899 context.scale(scale, scale);
28901 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28902 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28904 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28905 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28907 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28908 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28910 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28915 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28916 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28918 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28919 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28921 var targetWidth = this.minWidth - 2 * x;
28922 var targetHeight = this.minHeight - 2 * y;
28926 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28927 scale = targetWidth / width;
28930 if(x > 0 && y == 0){
28931 scale = targetHeight / height;
28934 if(x > 0 && y > 0){
28935 scale = targetWidth / width;
28937 if(width < height){
28938 scale = targetHeight / height;
28942 context.scale(scale, scale);
28944 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28945 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28947 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28948 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28950 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28952 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28959 this.cropData = canvas.toDataURL(this.cropType);
28961 if(this.fireEvent('crop', this, this.cropData) !== false){
28962 this.process(this.file, this.cropData);
28969 setThumbBoxSize : function()
28973 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28974 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28975 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28977 this.minWidth = width;
28978 this.minHeight = height;
28980 if(this.rotate == 90 || this.rotate == 270){
28981 this.minWidth = height;
28982 this.minHeight = width;
28987 width = Math.ceil(this.minWidth * height / this.minHeight);
28989 if(this.minWidth > this.minHeight){
28991 height = Math.ceil(this.minHeight * width / this.minWidth);
28994 this.thumbEl.setStyle({
28995 width : width + 'px',
28996 height : height + 'px'
29003 setThumbBoxPosition : function()
29005 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29006 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29008 this.thumbEl.setLeft(x);
29009 this.thumbEl.setTop(y);
29013 baseRotateLevel : function()
29015 this.baseRotate = 1;
29018 typeof(this.exif) != 'undefined' &&
29019 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29020 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29022 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29025 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29029 baseScaleLevel : function()
29033 if(this.isDocument){
29035 if(this.baseRotate == 6 || this.baseRotate == 8){
29037 height = this.thumbEl.getHeight();
29038 this.baseScale = height / this.imageEl.OriginWidth;
29040 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29041 width = this.thumbEl.getWidth();
29042 this.baseScale = width / this.imageEl.OriginHeight;
29048 height = this.thumbEl.getHeight();
29049 this.baseScale = height / this.imageEl.OriginHeight;
29051 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29052 width = this.thumbEl.getWidth();
29053 this.baseScale = width / this.imageEl.OriginWidth;
29059 if(this.baseRotate == 6 || this.baseRotate == 8){
29061 width = this.thumbEl.getHeight();
29062 this.baseScale = width / this.imageEl.OriginHeight;
29064 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29065 height = this.thumbEl.getWidth();
29066 this.baseScale = height / this.imageEl.OriginHeight;
29069 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29070 height = this.thumbEl.getWidth();
29071 this.baseScale = height / this.imageEl.OriginHeight;
29073 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29074 width = this.thumbEl.getHeight();
29075 this.baseScale = width / this.imageEl.OriginWidth;
29082 width = this.thumbEl.getWidth();
29083 this.baseScale = width / this.imageEl.OriginWidth;
29085 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29086 height = this.thumbEl.getHeight();
29087 this.baseScale = height / this.imageEl.OriginHeight;
29090 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29092 height = this.thumbEl.getHeight();
29093 this.baseScale = height / this.imageEl.OriginHeight;
29095 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29096 width = this.thumbEl.getWidth();
29097 this.baseScale = width / this.imageEl.OriginWidth;
29105 getScaleLevel : function()
29107 return this.baseScale * Math.pow(1.1, this.scale);
29110 onTouchStart : function(e)
29112 if(!this.canvasLoaded){
29113 this.beforeSelectFile(e);
29117 var touches = e.browserEvent.touches;
29123 if(touches.length == 1){
29124 this.onMouseDown(e);
29128 if(touches.length != 2){
29134 for(var i = 0, finger; finger = touches[i]; i++){
29135 coords.push(finger.pageX, finger.pageY);
29138 var x = Math.pow(coords[0] - coords[2], 2);
29139 var y = Math.pow(coords[1] - coords[3], 2);
29141 this.startDistance = Math.sqrt(x + y);
29143 this.startScale = this.scale;
29145 this.pinching = true;
29146 this.dragable = false;
29150 onTouchMove : function(e)
29152 if(!this.pinching && !this.dragable){
29156 var touches = e.browserEvent.touches;
29163 this.onMouseMove(e);
29169 for(var i = 0, finger; finger = touches[i]; i++){
29170 coords.push(finger.pageX, finger.pageY);
29173 var x = Math.pow(coords[0] - coords[2], 2);
29174 var y = Math.pow(coords[1] - coords[3], 2);
29176 this.endDistance = Math.sqrt(x + y);
29178 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29180 if(!this.zoomable()){
29181 this.scale = this.startScale;
29189 onTouchEnd : function(e)
29191 this.pinching = false;
29192 this.dragable = false;
29196 process : function(file, crop)
29199 this.maskEl.mask(this.loadingText);
29202 this.xhr = new XMLHttpRequest();
29204 file.xhr = this.xhr;
29206 this.xhr.open(this.method, this.url, true);
29209 "Accept": "application/json",
29210 "Cache-Control": "no-cache",
29211 "X-Requested-With": "XMLHttpRequest"
29214 for (var headerName in headers) {
29215 var headerValue = headers[headerName];
29217 this.xhr.setRequestHeader(headerName, headerValue);
29223 this.xhr.onload = function()
29225 _this.xhrOnLoad(_this.xhr);
29228 this.xhr.onerror = function()
29230 _this.xhrOnError(_this.xhr);
29233 var formData = new FormData();
29235 formData.append('returnHTML', 'NO');
29238 formData.append('crop', crop);
29241 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29242 formData.append(this.paramName, file, file.name);
29245 if(typeof(file.filename) != 'undefined'){
29246 formData.append('filename', file.filename);
29249 if(typeof(file.mimetype) != 'undefined'){
29250 formData.append('mimetype', file.mimetype);
29253 if(this.fireEvent('arrange', this, formData) != false){
29254 this.xhr.send(formData);
29258 xhrOnLoad : function(xhr)
29261 this.maskEl.unmask();
29264 if (xhr.readyState !== 4) {
29265 this.fireEvent('exception', this, xhr);
29269 var response = Roo.decode(xhr.responseText);
29271 if(!response.success){
29272 this.fireEvent('exception', this, xhr);
29276 var response = Roo.decode(xhr.responseText);
29278 this.fireEvent('upload', this, response);
29282 xhrOnError : function()
29285 this.maskEl.unmask();
29288 Roo.log('xhr on error');
29290 var response = Roo.decode(xhr.responseText);
29296 prepare : function(file)
29299 this.maskEl.mask(this.loadingText);
29305 if(typeof(file) === 'string'){
29306 this.loadCanvas(file);
29310 if(!file || !this.urlAPI){
29315 this.cropType = file.type;
29319 if(this.fireEvent('prepare', this, this.file) != false){
29321 var reader = new FileReader();
29323 reader.onload = function (e) {
29324 if (e.target.error) {
29325 Roo.log(e.target.error);
29329 var buffer = e.target.result,
29330 dataView = new DataView(buffer),
29332 maxOffset = dataView.byteLength - 4,
29336 if (dataView.getUint16(0) === 0xffd8) {
29337 while (offset < maxOffset) {
29338 markerBytes = dataView.getUint16(offset);
29340 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29341 markerLength = dataView.getUint16(offset + 2) + 2;
29342 if (offset + markerLength > dataView.byteLength) {
29343 Roo.log('Invalid meta data: Invalid segment size.');
29347 if(markerBytes == 0xffe1){
29348 _this.parseExifData(
29355 offset += markerLength;
29365 var url = _this.urlAPI.createObjectURL(_this.file);
29367 _this.loadCanvas(url);
29372 reader.readAsArrayBuffer(this.file);
29378 parseExifData : function(dataView, offset, length)
29380 var tiffOffset = offset + 10,
29384 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29385 // No Exif data, might be XMP data instead
29389 // Check for the ASCII code for "Exif" (0x45786966):
29390 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29391 // No Exif data, might be XMP data instead
29394 if (tiffOffset + 8 > dataView.byteLength) {
29395 Roo.log('Invalid Exif data: Invalid segment size.');
29398 // Check for the two null bytes:
29399 if (dataView.getUint16(offset + 8) !== 0x0000) {
29400 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29403 // Check the byte alignment:
29404 switch (dataView.getUint16(tiffOffset)) {
29406 littleEndian = true;
29409 littleEndian = false;
29412 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29415 // Check for the TIFF tag marker (0x002A):
29416 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29417 Roo.log('Invalid Exif data: Missing TIFF marker.');
29420 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29421 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29423 this.parseExifTags(
29426 tiffOffset + dirOffset,
29431 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29436 if (dirOffset + 6 > dataView.byteLength) {
29437 Roo.log('Invalid Exif data: Invalid directory offset.');
29440 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29441 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29442 if (dirEndOffset + 4 > dataView.byteLength) {
29443 Roo.log('Invalid Exif data: Invalid directory size.');
29446 for (i = 0; i < tagsNumber; i += 1) {
29450 dirOffset + 2 + 12 * i, // tag offset
29454 // Return the offset to the next directory:
29455 return dataView.getUint32(dirEndOffset, littleEndian);
29458 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29460 var tag = dataView.getUint16(offset, littleEndian);
29462 this.exif[tag] = this.getExifValue(
29466 dataView.getUint16(offset + 2, littleEndian), // tag type
29467 dataView.getUint32(offset + 4, littleEndian), // tag length
29472 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29474 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29483 Roo.log('Invalid Exif data: Invalid tag type.');
29487 tagSize = tagType.size * length;
29488 // Determine if the value is contained in the dataOffset bytes,
29489 // or if the value at the dataOffset is a pointer to the actual data:
29490 dataOffset = tagSize > 4 ?
29491 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29492 if (dataOffset + tagSize > dataView.byteLength) {
29493 Roo.log('Invalid Exif data: Invalid data offset.');
29496 if (length === 1) {
29497 return tagType.getValue(dataView, dataOffset, littleEndian);
29500 for (i = 0; i < length; i += 1) {
29501 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29504 if (tagType.ascii) {
29506 // Concatenate the chars:
29507 for (i = 0; i < values.length; i += 1) {
29509 // Ignore the terminating NULL byte(s):
29510 if (c === '\u0000') {
29522 Roo.apply(Roo.bootstrap.UploadCropbox, {
29524 'Orientation': 0x0112
29528 1: 0, //'top-left',
29530 3: 180, //'bottom-right',
29531 // 4: 'bottom-left',
29533 6: 90, //'right-top',
29534 // 7: 'right-bottom',
29535 8: 270 //'left-bottom'
29539 // byte, 8-bit unsigned int:
29541 getValue: function (dataView, dataOffset) {
29542 return dataView.getUint8(dataOffset);
29546 // ascii, 8-bit byte:
29548 getValue: function (dataView, dataOffset) {
29549 return String.fromCharCode(dataView.getUint8(dataOffset));
29554 // short, 16 bit int:
29556 getValue: function (dataView, dataOffset, littleEndian) {
29557 return dataView.getUint16(dataOffset, littleEndian);
29561 // long, 32 bit int:
29563 getValue: function (dataView, dataOffset, littleEndian) {
29564 return dataView.getUint32(dataOffset, littleEndian);
29568 // rational = two long values, first is numerator, second is denominator:
29570 getValue: function (dataView, dataOffset, littleEndian) {
29571 return dataView.getUint32(dataOffset, littleEndian) /
29572 dataView.getUint32(dataOffset + 4, littleEndian);
29576 // slong, 32 bit signed int:
29578 getValue: function (dataView, dataOffset, littleEndian) {
29579 return dataView.getInt32(dataOffset, littleEndian);
29583 // srational, two slongs, first is numerator, second is denominator:
29585 getValue: function (dataView, dataOffset, littleEndian) {
29586 return dataView.getInt32(dataOffset, littleEndian) /
29587 dataView.getInt32(dataOffset + 4, littleEndian);
29597 cls : 'btn-group roo-upload-cropbox-rotate-left',
29598 action : 'rotate-left',
29602 cls : 'btn btn-default',
29603 html : '<i class="fa fa-undo"></i>'
29609 cls : 'btn-group roo-upload-cropbox-picture',
29610 action : 'picture',
29614 cls : 'btn btn-default',
29615 html : '<i class="fa fa-picture-o"></i>'
29621 cls : 'btn-group roo-upload-cropbox-rotate-right',
29622 action : 'rotate-right',
29626 cls : 'btn btn-default',
29627 html : '<i class="fa fa-repeat"></i>'
29635 cls : 'btn-group roo-upload-cropbox-rotate-left',
29636 action : 'rotate-left',
29640 cls : 'btn btn-default',
29641 html : '<i class="fa fa-undo"></i>'
29647 cls : 'btn-group roo-upload-cropbox-download',
29648 action : 'download',
29652 cls : 'btn btn-default',
29653 html : '<i class="fa fa-download"></i>'
29659 cls : 'btn-group roo-upload-cropbox-crop',
29664 cls : 'btn btn-default',
29665 html : '<i class="fa fa-crop"></i>'
29671 cls : 'btn-group roo-upload-cropbox-trash',
29676 cls : 'btn btn-default',
29677 html : '<i class="fa fa-trash"></i>'
29683 cls : 'btn-group roo-upload-cropbox-rotate-right',
29684 action : 'rotate-right',
29688 cls : 'btn btn-default',
29689 html : '<i class="fa fa-repeat"></i>'
29697 cls : 'btn-group roo-upload-cropbox-rotate-left',
29698 action : 'rotate-left',
29702 cls : 'btn btn-default',
29703 html : '<i class="fa fa-undo"></i>'
29709 cls : 'btn-group roo-upload-cropbox-rotate-right',
29710 action : 'rotate-right',
29714 cls : 'btn btn-default',
29715 html : '<i class="fa fa-repeat"></i>'
29728 * @class Roo.bootstrap.DocumentManager
29729 * @extends Roo.bootstrap.Component
29730 * Bootstrap DocumentManager class
29731 * @cfg {String} paramName default 'imageUpload'
29732 * @cfg {String} toolTipName default 'filename'
29733 * @cfg {String} method default POST
29734 * @cfg {String} url action url
29735 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29736 * @cfg {Boolean} multiple multiple upload default true
29737 * @cfg {Number} thumbSize default 300
29738 * @cfg {String} fieldLabel
29739 * @cfg {Number} labelWidth default 4
29740 * @cfg {String} labelAlign (left|top) default left
29741 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29742 * @cfg {Number} labellg set the width of label (1-12)
29743 * @cfg {Number} labelmd set the width of label (1-12)
29744 * @cfg {Number} labelsm set the width of label (1-12)
29745 * @cfg {Number} labelxs set the width of label (1-12)
29748 * Create a new DocumentManager
29749 * @param {Object} config The config object
29752 Roo.bootstrap.DocumentManager = function(config){
29753 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29756 this.delegates = [];
29761 * Fire when initial the DocumentManager
29762 * @param {Roo.bootstrap.DocumentManager} this
29767 * inspect selected file
29768 * @param {Roo.bootstrap.DocumentManager} this
29769 * @param {File} file
29774 * Fire when xhr load exception
29775 * @param {Roo.bootstrap.DocumentManager} this
29776 * @param {XMLHttpRequest} xhr
29778 "exception" : true,
29780 * @event afterupload
29781 * Fire when xhr load exception
29782 * @param {Roo.bootstrap.DocumentManager} this
29783 * @param {XMLHttpRequest} xhr
29785 "afterupload" : true,
29788 * prepare the form data
29789 * @param {Roo.bootstrap.DocumentManager} this
29790 * @param {Object} formData
29795 * Fire when remove the file
29796 * @param {Roo.bootstrap.DocumentManager} this
29797 * @param {Object} file
29802 * Fire after refresh the file
29803 * @param {Roo.bootstrap.DocumentManager} this
29808 * Fire after click the image
29809 * @param {Roo.bootstrap.DocumentManager} this
29810 * @param {Object} file
29815 * Fire when upload a image and editable set to true
29816 * @param {Roo.bootstrap.DocumentManager} this
29817 * @param {Object} file
29821 * @event beforeselectfile
29822 * Fire before select file
29823 * @param {Roo.bootstrap.DocumentManager} this
29825 "beforeselectfile" : true,
29828 * Fire before process file
29829 * @param {Roo.bootstrap.DocumentManager} this
29830 * @param {Object} file
29834 * @event previewrendered
29835 * Fire when preview rendered
29836 * @param {Roo.bootstrap.DocumentManager} this
29837 * @param {Object} file
29839 "previewrendered" : true,
29842 "previewResize" : true
29847 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29856 paramName : 'imageUpload',
29857 toolTipName : 'filename',
29860 labelAlign : 'left',
29870 getAutoCreate : function()
29872 var managerWidget = {
29874 cls : 'roo-document-manager',
29878 cls : 'roo-document-manager-selector',
29883 cls : 'roo-document-manager-uploader',
29887 cls : 'roo-document-manager-upload-btn',
29888 html : '<i class="fa fa-plus"></i>'
29899 cls : 'column col-md-12',
29904 if(this.fieldLabel.length){
29909 cls : 'column col-md-12',
29910 html : this.fieldLabel
29914 cls : 'column col-md-12',
29919 if(this.labelAlign == 'left'){
29924 html : this.fieldLabel
29933 if(this.labelWidth > 12){
29934 content[0].style = "width: " + this.labelWidth + 'px';
29937 if(this.labelWidth < 13 && this.labelmd == 0){
29938 this.labelmd = this.labelWidth;
29941 if(this.labellg > 0){
29942 content[0].cls += ' col-lg-' + this.labellg;
29943 content[1].cls += ' col-lg-' + (12 - this.labellg);
29946 if(this.labelmd > 0){
29947 content[0].cls += ' col-md-' + this.labelmd;
29948 content[1].cls += ' col-md-' + (12 - this.labelmd);
29951 if(this.labelsm > 0){
29952 content[0].cls += ' col-sm-' + this.labelsm;
29953 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29956 if(this.labelxs > 0){
29957 content[0].cls += ' col-xs-' + this.labelxs;
29958 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29966 cls : 'row clearfix',
29974 initEvents : function()
29976 this.managerEl = this.el.select('.roo-document-manager', true).first();
29977 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29979 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29980 this.selectorEl.hide();
29983 this.selectorEl.attr('multiple', 'multiple');
29986 this.selectorEl.on('change', this.onFileSelected, this);
29988 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29989 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29991 this.uploader.on('click', this.onUploaderClick, this);
29993 this.renderProgressDialog();
29997 window.addEventListener("resize", function() { _this.refresh(); } );
29999 this.fireEvent('initial', this);
30002 renderProgressDialog : function()
30006 this.progressDialog = new Roo.bootstrap.Modal({
30007 cls : 'roo-document-manager-progress-dialog',
30008 allow_close : false,
30019 btnclick : function() {
30020 _this.uploadCancel();
30026 this.progressDialog.render(Roo.get(document.body));
30028 this.progress = new Roo.bootstrap.Progress({
30029 cls : 'roo-document-manager-progress',
30034 this.progress.render(this.progressDialog.getChildContainer());
30036 this.progressBar = new Roo.bootstrap.ProgressBar({
30037 cls : 'roo-document-manager-progress-bar',
30040 aria_valuemax : 12,
30044 this.progressBar.render(this.progress.getChildContainer());
30047 onUploaderClick : function(e)
30049 e.preventDefault();
30051 if(this.fireEvent('beforeselectfile', this) != false){
30052 this.selectorEl.dom.click();
30057 onFileSelected : function(e)
30059 e.preventDefault();
30061 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30065 Roo.each(this.selectorEl.dom.files, function(file){
30066 if(this.fireEvent('inspect', this, file) != false){
30067 this.files.push(file);
30077 this.selectorEl.dom.value = '';
30079 if(!this.files || !this.files.length){
30083 if(this.boxes > 0 && this.files.length > this.boxes){
30084 this.files = this.files.slice(0, this.boxes);
30087 this.uploader.show();
30089 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30090 this.uploader.hide();
30099 Roo.each(this.files, function(file){
30101 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30102 var f = this.renderPreview(file);
30107 if(file.type.indexOf('image') != -1){
30108 this.delegates.push(
30110 _this.process(file);
30111 }).createDelegate(this)
30119 _this.process(file);
30120 }).createDelegate(this)
30125 this.files = files;
30127 this.delegates = this.delegates.concat(docs);
30129 if(!this.delegates.length){
30134 this.progressBar.aria_valuemax = this.delegates.length;
30141 arrange : function()
30143 if(!this.delegates.length){
30144 this.progressDialog.hide();
30149 var delegate = this.delegates.shift();
30151 this.progressDialog.show();
30153 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30155 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30160 refresh : function()
30162 this.uploader.show();
30164 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30165 this.uploader.hide();
30168 Roo.isTouch ? this.closable(false) : this.closable(true);
30170 this.fireEvent('refresh', this);
30173 onRemove : function(e, el, o)
30175 e.preventDefault();
30177 this.fireEvent('remove', this, o);
30181 remove : function(o)
30185 Roo.each(this.files, function(file){
30186 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30195 this.files = files;
30202 Roo.each(this.files, function(file){
30207 file.target.remove();
30216 onClick : function(e, el, o)
30218 e.preventDefault();
30220 this.fireEvent('click', this, o);
30224 closable : function(closable)
30226 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30228 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30240 xhrOnLoad : function(xhr)
30242 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30246 if (xhr.readyState !== 4) {
30248 this.fireEvent('exception', this, xhr);
30252 var response = Roo.decode(xhr.responseText);
30254 if(!response.success){
30256 this.fireEvent('exception', this, xhr);
30260 var file = this.renderPreview(response.data);
30262 this.files.push(file);
30266 this.fireEvent('afterupload', this, xhr);
30270 xhrOnError : function(xhr)
30272 Roo.log('xhr on error');
30274 var response = Roo.decode(xhr.responseText);
30281 process : function(file)
30283 if(this.fireEvent('process', this, file) !== false){
30284 if(this.editable && file.type.indexOf('image') != -1){
30285 this.fireEvent('edit', this, file);
30289 this.uploadStart(file, false);
30296 uploadStart : function(file, crop)
30298 this.xhr = new XMLHttpRequest();
30300 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30305 file.xhr = this.xhr;
30307 this.managerEl.createChild({
30309 cls : 'roo-document-manager-loading',
30313 tooltip : file.name,
30314 cls : 'roo-document-manager-thumb',
30315 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30321 this.xhr.open(this.method, this.url, true);
30324 "Accept": "application/json",
30325 "Cache-Control": "no-cache",
30326 "X-Requested-With": "XMLHttpRequest"
30329 for (var headerName in headers) {
30330 var headerValue = headers[headerName];
30332 this.xhr.setRequestHeader(headerName, headerValue);
30338 this.xhr.onload = function()
30340 _this.xhrOnLoad(_this.xhr);
30343 this.xhr.onerror = function()
30345 _this.xhrOnError(_this.xhr);
30348 var formData = new FormData();
30350 formData.append('returnHTML', 'NO');
30353 formData.append('crop', crop);
30356 formData.append(this.paramName, file, file.name);
30363 if(this.fireEvent('prepare', this, formData, options) != false){
30365 if(options.manually){
30369 this.xhr.send(formData);
30373 this.uploadCancel();
30376 uploadCancel : function()
30382 this.delegates = [];
30384 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30391 renderPreview : function(file)
30393 if(typeof(file.target) != 'undefined' && file.target){
30397 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30399 var previewEl = this.managerEl.createChild({
30401 cls : 'roo-document-manager-preview',
30405 tooltip : file[this.toolTipName],
30406 cls : 'roo-document-manager-thumb',
30407 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30412 html : '<i class="fa fa-times-circle"></i>'
30417 var close = previewEl.select('button.close', true).first();
30419 close.on('click', this.onRemove, this, file);
30421 file.target = previewEl;
30423 var image = previewEl.select('img', true).first();
30427 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30429 image.on('click', this.onClick, this, file);
30431 this.fireEvent('previewrendered', this, file);
30437 onPreviewLoad : function(file, image)
30439 if(typeof(file.target) == 'undefined' || !file.target){
30443 var width = image.dom.naturalWidth || image.dom.width;
30444 var height = image.dom.naturalHeight || image.dom.height;
30446 if(!this.previewResize) {
30450 if(width > height){
30451 file.target.addClass('wide');
30455 file.target.addClass('tall');
30460 uploadFromSource : function(file, crop)
30462 this.xhr = new XMLHttpRequest();
30464 this.managerEl.createChild({
30466 cls : 'roo-document-manager-loading',
30470 tooltip : file.name,
30471 cls : 'roo-document-manager-thumb',
30472 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30478 this.xhr.open(this.method, this.url, true);
30481 "Accept": "application/json",
30482 "Cache-Control": "no-cache",
30483 "X-Requested-With": "XMLHttpRequest"
30486 for (var headerName in headers) {
30487 var headerValue = headers[headerName];
30489 this.xhr.setRequestHeader(headerName, headerValue);
30495 this.xhr.onload = function()
30497 _this.xhrOnLoad(_this.xhr);
30500 this.xhr.onerror = function()
30502 _this.xhrOnError(_this.xhr);
30505 var formData = new FormData();
30507 formData.append('returnHTML', 'NO');
30509 formData.append('crop', crop);
30511 if(typeof(file.filename) != 'undefined'){
30512 formData.append('filename', file.filename);
30515 if(typeof(file.mimetype) != 'undefined'){
30516 formData.append('mimetype', file.mimetype);
30521 if(this.fireEvent('prepare', this, formData) != false){
30522 this.xhr.send(formData);
30532 * @class Roo.bootstrap.DocumentViewer
30533 * @extends Roo.bootstrap.Component
30534 * Bootstrap DocumentViewer class
30535 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30536 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30539 * Create a new DocumentViewer
30540 * @param {Object} config The config object
30543 Roo.bootstrap.DocumentViewer = function(config){
30544 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30549 * Fire after initEvent
30550 * @param {Roo.bootstrap.DocumentViewer} this
30556 * @param {Roo.bootstrap.DocumentViewer} this
30561 * Fire after download button
30562 * @param {Roo.bootstrap.DocumentViewer} this
30567 * Fire after trash button
30568 * @param {Roo.bootstrap.DocumentViewer} this
30575 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30577 showDownload : true,
30581 getAutoCreate : function()
30585 cls : 'roo-document-viewer',
30589 cls : 'roo-document-viewer-body',
30593 cls : 'roo-document-viewer-thumb',
30597 cls : 'roo-document-viewer-image'
30605 cls : 'roo-document-viewer-footer',
30608 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30612 cls : 'btn-group roo-document-viewer-download',
30616 cls : 'btn btn-default',
30617 html : '<i class="fa fa-download"></i>'
30623 cls : 'btn-group roo-document-viewer-trash',
30627 cls : 'btn btn-default',
30628 html : '<i class="fa fa-trash"></i>'
30641 initEvents : function()
30643 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30644 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30646 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30647 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30649 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30650 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30652 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30653 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30655 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30656 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30658 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30659 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30661 this.bodyEl.on('click', this.onClick, this);
30662 this.downloadBtn.on('click', this.onDownload, this);
30663 this.trashBtn.on('click', this.onTrash, this);
30665 this.downloadBtn.hide();
30666 this.trashBtn.hide();
30668 if(this.showDownload){
30669 this.downloadBtn.show();
30672 if(this.showTrash){
30673 this.trashBtn.show();
30676 if(!this.showDownload && !this.showTrash) {
30677 this.footerEl.hide();
30682 initial : function()
30684 this.fireEvent('initial', this);
30688 onClick : function(e)
30690 e.preventDefault();
30692 this.fireEvent('click', this);
30695 onDownload : function(e)
30697 e.preventDefault();
30699 this.fireEvent('download', this);
30702 onTrash : function(e)
30704 e.preventDefault();
30706 this.fireEvent('trash', this);
30718 * @class Roo.bootstrap.NavProgressBar
30719 * @extends Roo.bootstrap.Component
30720 * Bootstrap NavProgressBar class
30723 * Create a new nav progress bar
30724 * @param {Object} config The config object
30727 Roo.bootstrap.NavProgressBar = function(config){
30728 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30730 this.bullets = this.bullets || [];
30732 // Roo.bootstrap.NavProgressBar.register(this);
30736 * Fires when the active item changes
30737 * @param {Roo.bootstrap.NavProgressBar} this
30738 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30739 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30746 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30751 getAutoCreate : function()
30753 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30757 cls : 'roo-navigation-bar-group',
30761 cls : 'roo-navigation-top-bar'
30765 cls : 'roo-navigation-bullets-bar',
30769 cls : 'roo-navigation-bar'
30776 cls : 'roo-navigation-bottom-bar'
30786 initEvents: function()
30791 onRender : function(ct, position)
30793 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30795 if(this.bullets.length){
30796 Roo.each(this.bullets, function(b){
30805 addItem : function(cfg)
30807 var item = new Roo.bootstrap.NavProgressItem(cfg);
30809 item.parentId = this.id;
30810 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30813 var top = new Roo.bootstrap.Element({
30815 cls : 'roo-navigation-bar-text'
30818 var bottom = new Roo.bootstrap.Element({
30820 cls : 'roo-navigation-bar-text'
30823 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30824 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30826 var topText = new Roo.bootstrap.Element({
30828 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30831 var bottomText = new Roo.bootstrap.Element({
30833 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30836 topText.onRender(top.el, null);
30837 bottomText.onRender(bottom.el, null);
30840 item.bottomEl = bottom;
30843 this.barItems.push(item);
30848 getActive : function()
30850 var active = false;
30852 Roo.each(this.barItems, function(v){
30854 if (!v.isActive()) {
30866 setActiveItem : function(item)
30870 Roo.each(this.barItems, function(v){
30871 if (v.rid == item.rid) {
30875 if (v.isActive()) {
30876 v.setActive(false);
30881 item.setActive(true);
30883 this.fireEvent('changed', this, item, prev);
30886 getBarItem: function(rid)
30890 Roo.each(this.barItems, function(e) {
30891 if (e.rid != rid) {
30902 indexOfItem : function(item)
30906 Roo.each(this.barItems, function(v, i){
30908 if (v.rid != item.rid) {
30919 setActiveNext : function()
30921 var i = this.indexOfItem(this.getActive());
30923 if (i > this.barItems.length) {
30927 this.setActiveItem(this.barItems[i+1]);
30930 setActivePrev : function()
30932 var i = this.indexOfItem(this.getActive());
30938 this.setActiveItem(this.barItems[i-1]);
30941 format : function()
30943 if(!this.barItems.length){
30947 var width = 100 / this.barItems.length;
30949 Roo.each(this.barItems, function(i){
30950 i.el.setStyle('width', width + '%');
30951 i.topEl.el.setStyle('width', width + '%');
30952 i.bottomEl.el.setStyle('width', width + '%');
30961 * Nav Progress Item
30966 * @class Roo.bootstrap.NavProgressItem
30967 * @extends Roo.bootstrap.Component
30968 * Bootstrap NavProgressItem class
30969 * @cfg {String} rid the reference id
30970 * @cfg {Boolean} active (true|false) Is item active default false
30971 * @cfg {Boolean} disabled (true|false) Is item active default false
30972 * @cfg {String} html
30973 * @cfg {String} position (top|bottom) text position default bottom
30974 * @cfg {String} icon show icon instead of number
30977 * Create a new NavProgressItem
30978 * @param {Object} config The config object
30980 Roo.bootstrap.NavProgressItem = function(config){
30981 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30986 * The raw click event for the entire grid.
30987 * @param {Roo.bootstrap.NavProgressItem} this
30988 * @param {Roo.EventObject} e
30995 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
31001 position : 'bottom',
31004 getAutoCreate : function()
31006 var iconCls = 'roo-navigation-bar-item-icon';
31008 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31012 cls: 'roo-navigation-bar-item',
31022 cfg.cls += ' active';
31025 cfg.cls += ' disabled';
31031 disable : function()
31033 this.setDisabled(true);
31036 enable : function()
31038 this.setDisabled(false);
31041 initEvents: function()
31043 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31045 this.iconEl.on('click', this.onClick, this);
31048 onClick : function(e)
31050 e.preventDefault();
31056 if(this.fireEvent('click', this, e) === false){
31060 this.parent().setActiveItem(this);
31063 isActive: function ()
31065 return this.active;
31068 setActive : function(state)
31070 if(this.active == state){
31074 this.active = state;
31077 this.el.addClass('active');
31081 this.el.removeClass('active');
31086 setDisabled : function(state)
31088 if(this.disabled == state){
31092 this.disabled = state;
31095 this.el.addClass('disabled');
31099 this.el.removeClass('disabled');
31102 tooltipEl : function()
31104 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31117 * @class Roo.bootstrap.FieldLabel
31118 * @extends Roo.bootstrap.Component
31119 * Bootstrap FieldLabel class
31120 * @cfg {String} html contents of the element
31121 * @cfg {String} tag tag of the element default label
31122 * @cfg {String} cls class of the element
31123 * @cfg {String} target label target
31124 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31125 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31126 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31127 * @cfg {String} iconTooltip default "This field is required"
31128 * @cfg {String} indicatorpos (left|right) default left
31131 * Create a new FieldLabel
31132 * @param {Object} config The config object
31135 Roo.bootstrap.FieldLabel = function(config){
31136 Roo.bootstrap.Element.superclass.constructor.call(this, config);
31141 * Fires after the field has been marked as invalid.
31142 * @param {Roo.form.FieldLabel} this
31143 * @param {String} msg The validation message
31148 * Fires after the field has been validated with no errors.
31149 * @param {Roo.form.FieldLabel} this
31155 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
31162 invalidClass : 'has-warning',
31163 validClass : 'has-success',
31164 iconTooltip : 'This field is required',
31165 indicatorpos : 'left',
31167 getAutoCreate : function(){
31170 if (!this.allowBlank) {
31176 cls : 'roo-bootstrap-field-label ' + this.cls,
31181 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31182 tooltip : this.iconTooltip
31191 if(this.indicatorpos == 'right'){
31194 cls : 'roo-bootstrap-field-label ' + this.cls,
31203 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31204 tooltip : this.iconTooltip
31213 initEvents: function()
31215 Roo.bootstrap.Element.superclass.initEvents.call(this);
31217 this.indicator = this.indicatorEl();
31219 if(this.indicator){
31220 this.indicator.removeClass('visible');
31221 this.indicator.addClass('invisible');
31224 Roo.bootstrap.FieldLabel.register(this);
31227 indicatorEl : function()
31229 var indicator = this.el.select('i.roo-required-indicator',true).first();
31240 * Mark this field as valid
31242 markValid : function()
31244 if(this.indicator){
31245 this.indicator.removeClass('visible');
31246 this.indicator.addClass('invisible');
31248 if (Roo.bootstrap.version == 3) {
31249 this.el.removeClass(this.invalidClass);
31250 this.el.addClass(this.validClass);
31252 this.el.removeClass('is-invalid');
31253 this.el.addClass('is-valid');
31257 this.fireEvent('valid', this);
31261 * Mark this field as invalid
31262 * @param {String} msg The validation message
31264 markInvalid : function(msg)
31266 if(this.indicator){
31267 this.indicator.removeClass('invisible');
31268 this.indicator.addClass('visible');
31270 if (Roo.bootstrap.version == 3) {
31271 this.el.removeClass(this.validClass);
31272 this.el.addClass(this.invalidClass);
31274 this.el.removeClass('is-valid');
31275 this.el.addClass('is-invalid');
31279 this.fireEvent('invalid', this, msg);
31285 Roo.apply(Roo.bootstrap.FieldLabel, {
31290 * register a FieldLabel Group
31291 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31293 register : function(label)
31295 if(this.groups.hasOwnProperty(label.target)){
31299 this.groups[label.target] = label;
31303 * fetch a FieldLabel Group based on the target
31304 * @param {string} target
31305 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31307 get: function(target) {
31308 if (typeof(this.groups[target]) == 'undefined') {
31312 return this.groups[target] ;
31321 * page DateSplitField.
31327 * @class Roo.bootstrap.DateSplitField
31328 * @extends Roo.bootstrap.Component
31329 * Bootstrap DateSplitField class
31330 * @cfg {string} fieldLabel - the label associated
31331 * @cfg {Number} labelWidth set the width of label (0-12)
31332 * @cfg {String} labelAlign (top|left)
31333 * @cfg {Boolean} dayAllowBlank (true|false) default false
31334 * @cfg {Boolean} monthAllowBlank (true|false) default false
31335 * @cfg {Boolean} yearAllowBlank (true|false) default false
31336 * @cfg {string} dayPlaceholder
31337 * @cfg {string} monthPlaceholder
31338 * @cfg {string} yearPlaceholder
31339 * @cfg {string} dayFormat default 'd'
31340 * @cfg {string} monthFormat default 'm'
31341 * @cfg {string} yearFormat default 'Y'
31342 * @cfg {Number} labellg set the width of label (1-12)
31343 * @cfg {Number} labelmd set the width of label (1-12)
31344 * @cfg {Number} labelsm set the width of label (1-12)
31345 * @cfg {Number} labelxs set the width of label (1-12)
31349 * Create a new DateSplitField
31350 * @param {Object} config The config object
31353 Roo.bootstrap.DateSplitField = function(config){
31354 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31360 * getting the data of years
31361 * @param {Roo.bootstrap.DateSplitField} this
31362 * @param {Object} years
31367 * getting the data of days
31368 * @param {Roo.bootstrap.DateSplitField} this
31369 * @param {Object} days
31374 * Fires after the field has been marked as invalid.
31375 * @param {Roo.form.Field} this
31376 * @param {String} msg The validation message
31381 * Fires after the field has been validated with no errors.
31382 * @param {Roo.form.Field} this
31388 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31391 labelAlign : 'top',
31393 dayAllowBlank : false,
31394 monthAllowBlank : false,
31395 yearAllowBlank : false,
31396 dayPlaceholder : '',
31397 monthPlaceholder : '',
31398 yearPlaceholder : '',
31402 isFormField : true,
31408 getAutoCreate : function()
31412 cls : 'row roo-date-split-field-group',
31417 cls : 'form-hidden-field roo-date-split-field-group-value',
31423 var labelCls = 'col-md-12';
31424 var contentCls = 'col-md-4';
31426 if(this.fieldLabel){
31430 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31434 html : this.fieldLabel
31439 if(this.labelAlign == 'left'){
31441 if(this.labelWidth > 12){
31442 label.style = "width: " + this.labelWidth + 'px';
31445 if(this.labelWidth < 13 && this.labelmd == 0){
31446 this.labelmd = this.labelWidth;
31449 if(this.labellg > 0){
31450 labelCls = ' col-lg-' + this.labellg;
31451 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31454 if(this.labelmd > 0){
31455 labelCls = ' col-md-' + this.labelmd;
31456 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31459 if(this.labelsm > 0){
31460 labelCls = ' col-sm-' + this.labelsm;
31461 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31464 if(this.labelxs > 0){
31465 labelCls = ' col-xs-' + this.labelxs;
31466 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31470 label.cls += ' ' + labelCls;
31472 cfg.cn.push(label);
31475 Roo.each(['day', 'month', 'year'], function(t){
31478 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31485 inputEl: function ()
31487 return this.el.select('.roo-date-split-field-group-value', true).first();
31490 onRender : function(ct, position)
31494 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31496 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31498 this.dayField = new Roo.bootstrap.ComboBox({
31499 allowBlank : this.dayAllowBlank,
31500 alwaysQuery : true,
31501 displayField : 'value',
31504 forceSelection : true,
31506 placeholder : this.dayPlaceholder,
31507 selectOnFocus : true,
31508 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31509 triggerAction : 'all',
31511 valueField : 'value',
31512 store : new Roo.data.SimpleStore({
31513 data : (function() {
31515 _this.fireEvent('days', _this, days);
31518 fields : [ 'value' ]
31521 select : function (_self, record, index)
31523 _this.setValue(_this.getValue());
31528 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31530 this.monthField = new Roo.bootstrap.MonthField({
31531 after : '<i class=\"fa fa-calendar\"></i>',
31532 allowBlank : this.monthAllowBlank,
31533 placeholder : this.monthPlaceholder,
31536 render : function (_self)
31538 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31539 e.preventDefault();
31543 select : function (_self, oldvalue, newvalue)
31545 _this.setValue(_this.getValue());
31550 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31552 this.yearField = new Roo.bootstrap.ComboBox({
31553 allowBlank : this.yearAllowBlank,
31554 alwaysQuery : true,
31555 displayField : 'value',
31558 forceSelection : true,
31560 placeholder : this.yearPlaceholder,
31561 selectOnFocus : true,
31562 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31563 triggerAction : 'all',
31565 valueField : 'value',
31566 store : new Roo.data.SimpleStore({
31567 data : (function() {
31569 _this.fireEvent('years', _this, years);
31572 fields : [ 'value' ]
31575 select : function (_self, record, index)
31577 _this.setValue(_this.getValue());
31582 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31585 setValue : function(v, format)
31587 this.inputEl.dom.value = v;
31589 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31591 var d = Date.parseDate(v, f);
31598 this.setDay(d.format(this.dayFormat));
31599 this.setMonth(d.format(this.monthFormat));
31600 this.setYear(d.format(this.yearFormat));
31607 setDay : function(v)
31609 this.dayField.setValue(v);
31610 this.inputEl.dom.value = this.getValue();
31615 setMonth : function(v)
31617 this.monthField.setValue(v, true);
31618 this.inputEl.dom.value = this.getValue();
31623 setYear : function(v)
31625 this.yearField.setValue(v);
31626 this.inputEl.dom.value = this.getValue();
31631 getDay : function()
31633 return this.dayField.getValue();
31636 getMonth : function()
31638 return this.monthField.getValue();
31641 getYear : function()
31643 return this.yearField.getValue();
31646 getValue : function()
31648 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31650 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31660 this.inputEl.dom.value = '';
31665 validate : function()
31667 var d = this.dayField.validate();
31668 var m = this.monthField.validate();
31669 var y = this.yearField.validate();
31674 (!this.dayAllowBlank && !d) ||
31675 (!this.monthAllowBlank && !m) ||
31676 (!this.yearAllowBlank && !y)
31681 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31690 this.markInvalid();
31695 markValid : function()
31698 var label = this.el.select('label', true).first();
31699 var icon = this.el.select('i.fa-star', true).first();
31705 this.fireEvent('valid', this);
31709 * Mark this field as invalid
31710 * @param {String} msg The validation message
31712 markInvalid : function(msg)
31715 var label = this.el.select('label', true).first();
31716 var icon = this.el.select('i.fa-star', true).first();
31718 if(label && !icon){
31719 this.el.select('.roo-date-split-field-label', true).createChild({
31721 cls : 'text-danger fa fa-lg fa-star',
31722 tooltip : 'This field is required',
31723 style : 'margin-right:5px;'
31727 this.fireEvent('invalid', this, msg);
31730 clearInvalid : function()
31732 var label = this.el.select('label', true).first();
31733 var icon = this.el.select('i.fa-star', true).first();
31739 this.fireEvent('valid', this);
31742 getName: function()
31752 * http://masonry.desandro.com
31754 * The idea is to render all the bricks based on vertical width...
31756 * The original code extends 'outlayer' - we might need to use that....
31762 * @class Roo.bootstrap.LayoutMasonry
31763 * @extends Roo.bootstrap.Component
31764 * Bootstrap Layout Masonry class
31767 * Create a new Element
31768 * @param {Object} config The config object
31771 Roo.bootstrap.LayoutMasonry = function(config){
31773 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31777 Roo.bootstrap.LayoutMasonry.register(this);
31783 * Fire after layout the items
31784 * @param {Roo.bootstrap.LayoutMasonry} this
31785 * @param {Roo.EventObject} e
31792 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31795 * @cfg {Boolean} isLayoutInstant = no animation?
31797 isLayoutInstant : false, // needed?
31800 * @cfg {Number} boxWidth width of the columns
31805 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31810 * @cfg {Number} padWidth padding below box..
31815 * @cfg {Number} gutter gutter width..
31820 * @cfg {Number} maxCols maximum number of columns
31826 * @cfg {Boolean} isAutoInitial defalut true
31828 isAutoInitial : true,
31833 * @cfg {Boolean} isHorizontal defalut false
31835 isHorizontal : false,
31837 currentSize : null,
31843 bricks: null, //CompositeElement
31847 _isLayoutInited : false,
31849 // isAlternative : false, // only use for vertical layout...
31852 * @cfg {Number} alternativePadWidth padding below box..
31854 alternativePadWidth : 50,
31856 selectedBrick : [],
31858 getAutoCreate : function(){
31860 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31864 cls: 'blog-masonary-wrapper ' + this.cls,
31866 cls : 'mas-boxes masonary'
31873 getChildContainer: function( )
31875 if (this.boxesEl) {
31876 return this.boxesEl;
31879 this.boxesEl = this.el.select('.mas-boxes').first();
31881 return this.boxesEl;
31885 initEvents : function()
31889 if(this.isAutoInitial){
31890 Roo.log('hook children rendered');
31891 this.on('childrenrendered', function() {
31892 Roo.log('children rendered');
31898 initial : function()
31900 this.selectedBrick = [];
31902 this.currentSize = this.el.getBox(true);
31904 Roo.EventManager.onWindowResize(this.resize, this);
31906 if(!this.isAutoInitial){
31914 //this.layout.defer(500,this);
31918 resize : function()
31920 var cs = this.el.getBox(true);
31923 this.currentSize.width == cs.width &&
31924 this.currentSize.x == cs.x &&
31925 this.currentSize.height == cs.height &&
31926 this.currentSize.y == cs.y
31928 Roo.log("no change in with or X or Y");
31932 this.currentSize = cs;
31938 layout : function()
31940 this._resetLayout();
31942 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31944 this.layoutItems( isInstant );
31946 this._isLayoutInited = true;
31948 this.fireEvent('layout', this);
31952 _resetLayout : function()
31954 if(this.isHorizontal){
31955 this.horizontalMeasureColumns();
31959 this.verticalMeasureColumns();
31963 verticalMeasureColumns : function()
31965 this.getContainerWidth();
31967 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31968 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31972 var boxWidth = this.boxWidth + this.padWidth;
31974 if(this.containerWidth < this.boxWidth){
31975 boxWidth = this.containerWidth
31978 var containerWidth = this.containerWidth;
31980 var cols = Math.floor(containerWidth / boxWidth);
31982 this.cols = Math.max( cols, 1 );
31984 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31986 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31988 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31990 this.colWidth = boxWidth + avail - this.padWidth;
31992 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31993 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31996 horizontalMeasureColumns : function()
31998 this.getContainerWidth();
32000 var boxWidth = this.boxWidth;
32002 if(this.containerWidth < boxWidth){
32003 boxWidth = this.containerWidth;
32006 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32008 this.el.setHeight(boxWidth);
32012 getContainerWidth : function()
32014 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32017 layoutItems : function( isInstant )
32019 Roo.log(this.bricks);
32021 var items = Roo.apply([], this.bricks);
32023 if(this.isHorizontal){
32024 this._horizontalLayoutItems( items , isInstant );
32028 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32029 // this._verticalAlternativeLayoutItems( items , isInstant );
32033 this._verticalLayoutItems( items , isInstant );
32037 _verticalLayoutItems : function ( items , isInstant)
32039 if ( !items || !items.length ) {
32044 ['xs', 'xs', 'xs', 'tall'],
32045 ['xs', 'xs', 'tall'],
32046 ['xs', 'xs', 'sm'],
32047 ['xs', 'xs', 'xs'],
32053 ['sm', 'xs', 'xs'],
32057 ['tall', 'xs', 'xs', 'xs'],
32058 ['tall', 'xs', 'xs'],
32070 Roo.each(items, function(item, k){
32072 switch (item.size) {
32073 // these layouts take up a full box,
32084 boxes.push([item]);
32107 var filterPattern = function(box, length)
32115 var pattern = box.slice(0, length);
32119 Roo.each(pattern, function(i){
32120 format.push(i.size);
32123 Roo.each(standard, function(s){
32125 if(String(s) != String(format)){
32134 if(!match && length == 1){
32139 filterPattern(box, length - 1);
32143 queue.push(pattern);
32145 box = box.slice(length, box.length);
32147 filterPattern(box, 4);
32153 Roo.each(boxes, function(box, k){
32159 if(box.length == 1){
32164 filterPattern(box, 4);
32168 this._processVerticalLayoutQueue( queue, isInstant );
32172 // _verticalAlternativeLayoutItems : function( items , isInstant )
32174 // if ( !items || !items.length ) {
32178 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
32182 _horizontalLayoutItems : function ( items , isInstant)
32184 if ( !items || !items.length || items.length < 3) {
32190 var eItems = items.slice(0, 3);
32192 items = items.slice(3, items.length);
32195 ['xs', 'xs', 'xs', 'wide'],
32196 ['xs', 'xs', 'wide'],
32197 ['xs', 'xs', 'sm'],
32198 ['xs', 'xs', 'xs'],
32204 ['sm', 'xs', 'xs'],
32208 ['wide', 'xs', 'xs', 'xs'],
32209 ['wide', 'xs', 'xs'],
32222 Roo.each(items, function(item, k){
32224 switch (item.size) {
32235 boxes.push([item]);
32259 var filterPattern = function(box, length)
32267 var pattern = box.slice(0, length);
32271 Roo.each(pattern, function(i){
32272 format.push(i.size);
32275 Roo.each(standard, function(s){
32277 if(String(s) != String(format)){
32286 if(!match && length == 1){
32291 filterPattern(box, length - 1);
32295 queue.push(pattern);
32297 box = box.slice(length, box.length);
32299 filterPattern(box, 4);
32305 Roo.each(boxes, function(box, k){
32311 if(box.length == 1){
32316 filterPattern(box, 4);
32323 var pos = this.el.getBox(true);
32327 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32329 var hit_end = false;
32331 Roo.each(queue, function(box){
32335 Roo.each(box, function(b){
32337 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32347 Roo.each(box, function(b){
32349 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32352 mx = Math.max(mx, b.x);
32356 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32360 Roo.each(box, function(b){
32362 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32376 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32379 /** Sets position of item in DOM
32380 * @param {Element} item
32381 * @param {Number} x - horizontal position
32382 * @param {Number} y - vertical position
32383 * @param {Boolean} isInstant - disables transitions
32385 _processVerticalLayoutQueue : function( queue, isInstant )
32387 var pos = this.el.getBox(true);
32392 for (var i = 0; i < this.cols; i++){
32396 Roo.each(queue, function(box, k){
32398 var col = k % this.cols;
32400 Roo.each(box, function(b,kk){
32402 b.el.position('absolute');
32404 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32405 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32407 if(b.size == 'md-left' || b.size == 'md-right'){
32408 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32409 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32412 b.el.setWidth(width);
32413 b.el.setHeight(height);
32415 b.el.select('iframe',true).setSize(width,height);
32419 for (var i = 0; i < this.cols; i++){
32421 if(maxY[i] < maxY[col]){
32426 col = Math.min(col, i);
32430 x = pos.x + col * (this.colWidth + this.padWidth);
32434 var positions = [];
32436 switch (box.length){
32438 positions = this.getVerticalOneBoxColPositions(x, y, box);
32441 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32444 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32447 positions = this.getVerticalFourBoxColPositions(x, y, box);
32453 Roo.each(box, function(b,kk){
32455 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32457 var sz = b.el.getSize();
32459 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32467 for (var i = 0; i < this.cols; i++){
32468 mY = Math.max(mY, maxY[i]);
32471 this.el.setHeight(mY - pos.y);
32475 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32477 // var pos = this.el.getBox(true);
32480 // var maxX = pos.right;
32482 // var maxHeight = 0;
32484 // Roo.each(items, function(item, k){
32488 // item.el.position('absolute');
32490 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32492 // item.el.setWidth(width);
32494 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32496 // item.el.setHeight(height);
32499 // item.el.setXY([x, y], isInstant ? false : true);
32501 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32504 // y = y + height + this.alternativePadWidth;
32506 // maxHeight = maxHeight + height + this.alternativePadWidth;
32510 // this.el.setHeight(maxHeight);
32514 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32516 var pos = this.el.getBox(true);
32521 var maxX = pos.right;
32523 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32525 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32527 Roo.each(queue, function(box, k){
32529 Roo.each(box, function(b, kk){
32531 b.el.position('absolute');
32533 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32534 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32536 if(b.size == 'md-left' || b.size == 'md-right'){
32537 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32538 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32541 b.el.setWidth(width);
32542 b.el.setHeight(height);
32550 var positions = [];
32552 switch (box.length){
32554 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32557 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32560 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32563 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32569 Roo.each(box, function(b,kk){
32571 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32573 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32581 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32583 Roo.each(eItems, function(b,k){
32585 b.size = (k == 0) ? 'sm' : 'xs';
32586 b.x = (k == 0) ? 2 : 1;
32587 b.y = (k == 0) ? 2 : 1;
32589 b.el.position('absolute');
32591 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32593 b.el.setWidth(width);
32595 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32597 b.el.setHeight(height);
32601 var positions = [];
32604 x : maxX - this.unitWidth * 2 - this.gutter,
32609 x : maxX - this.unitWidth,
32610 y : minY + (this.unitWidth + this.gutter) * 2
32614 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32618 Roo.each(eItems, function(b,k){
32620 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32626 getVerticalOneBoxColPositions : function(x, y, box)
32630 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32632 if(box[0].size == 'md-left'){
32636 if(box[0].size == 'md-right'){
32641 x : x + (this.unitWidth + this.gutter) * rand,
32648 getVerticalTwoBoxColPositions : function(x, y, box)
32652 if(box[0].size == 'xs'){
32656 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32660 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32674 x : x + (this.unitWidth + this.gutter) * 2,
32675 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32682 getVerticalThreeBoxColPositions : function(x, y, box)
32686 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32694 x : x + (this.unitWidth + this.gutter) * 1,
32699 x : x + (this.unitWidth + this.gutter) * 2,
32707 if(box[0].size == 'xs' && box[1].size == 'xs'){
32716 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32720 x : x + (this.unitWidth + this.gutter) * 1,
32734 x : x + (this.unitWidth + this.gutter) * 2,
32739 x : x + (this.unitWidth + this.gutter) * 2,
32740 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32747 getVerticalFourBoxColPositions : function(x, y, box)
32751 if(box[0].size == 'xs'){
32760 y : y + (this.unitHeight + this.gutter) * 1
32765 y : y + (this.unitHeight + this.gutter) * 2
32769 x : x + (this.unitWidth + this.gutter) * 1,
32783 x : x + (this.unitWidth + this.gutter) * 2,
32788 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32789 y : y + (this.unitHeight + this.gutter) * 1
32793 x : x + (this.unitWidth + this.gutter) * 2,
32794 y : y + (this.unitWidth + this.gutter) * 2
32801 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32805 if(box[0].size == 'md-left'){
32807 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32814 if(box[0].size == 'md-right'){
32816 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32817 y : minY + (this.unitWidth + this.gutter) * 1
32823 var rand = Math.floor(Math.random() * (4 - box[0].y));
32826 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32827 y : minY + (this.unitWidth + this.gutter) * rand
32834 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32838 if(box[0].size == 'xs'){
32841 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32846 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32847 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32855 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32860 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32861 y : minY + (this.unitWidth + this.gutter) * 2
32868 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32872 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32875 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32880 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32881 y : minY + (this.unitWidth + this.gutter) * 1
32885 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32886 y : minY + (this.unitWidth + this.gutter) * 2
32893 if(box[0].size == 'xs' && box[1].size == 'xs'){
32896 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32901 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32906 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32907 y : minY + (this.unitWidth + this.gutter) * 1
32915 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32920 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32921 y : minY + (this.unitWidth + this.gutter) * 2
32925 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32926 y : minY + (this.unitWidth + this.gutter) * 2
32933 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32937 if(box[0].size == 'xs'){
32940 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32945 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32950 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),
32955 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32956 y : minY + (this.unitWidth + this.gutter) * 1
32964 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32969 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32970 y : minY + (this.unitWidth + this.gutter) * 2
32974 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32975 y : minY + (this.unitWidth + this.gutter) * 2
32979 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),
32980 y : minY + (this.unitWidth + this.gutter) * 2
32988 * remove a Masonry Brick
32989 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32991 removeBrick : function(brick_id)
32997 for (var i = 0; i<this.bricks.length; i++) {
32998 if (this.bricks[i].id == brick_id) {
32999 this.bricks.splice(i,1);
33000 this.el.dom.removeChild(Roo.get(brick_id).dom);
33007 * adds a Masonry Brick
33008 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33010 addBrick : function(cfg)
33012 var cn = new Roo.bootstrap.MasonryBrick(cfg);
33013 //this.register(cn);
33014 cn.parentId = this.id;
33015 cn.render(this.el);
33020 * register a Masonry Brick
33021 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33024 register : function(brick)
33026 this.bricks.push(brick);
33027 brick.masonryId = this.id;
33031 * clear all the Masonry Brick
33033 clearAll : function()
33036 //this.getChildContainer().dom.innerHTML = "";
33037 this.el.dom.innerHTML = '';
33040 getSelected : function()
33042 if (!this.selectedBrick) {
33046 return this.selectedBrick;
33050 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33054 * register a Masonry Layout
33055 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33058 register : function(layout)
33060 this.groups[layout.id] = layout;
33063 * fetch a Masonry Layout based on the masonry layout ID
33064 * @param {string} the masonry layout to add
33065 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33068 get: function(layout_id) {
33069 if (typeof(this.groups[layout_id]) == 'undefined') {
33072 return this.groups[layout_id] ;
33084 * http://masonry.desandro.com
33086 * The idea is to render all the bricks based on vertical width...
33088 * The original code extends 'outlayer' - we might need to use that....
33094 * @class Roo.bootstrap.LayoutMasonryAuto
33095 * @extends Roo.bootstrap.Component
33096 * Bootstrap Layout Masonry class
33099 * Create a new Element
33100 * @param {Object} config The config object
33103 Roo.bootstrap.LayoutMasonryAuto = function(config){
33104 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33107 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
33110 * @cfg {Boolean} isFitWidth - resize the width..
33112 isFitWidth : false, // options..
33114 * @cfg {Boolean} isOriginLeft = left align?
33116 isOriginLeft : true,
33118 * @cfg {Boolean} isOriginTop = top align?
33120 isOriginTop : false,
33122 * @cfg {Boolean} isLayoutInstant = no animation?
33124 isLayoutInstant : false, // needed?
33126 * @cfg {Boolean} isResizingContainer = not sure if this is used..
33128 isResizingContainer : true,
33130 * @cfg {Number} columnWidth width of the columns
33136 * @cfg {Number} maxCols maximum number of columns
33141 * @cfg {Number} padHeight padding below box..
33147 * @cfg {Boolean} isAutoInitial defalut true
33150 isAutoInitial : true,
33156 initialColumnWidth : 0,
33157 currentSize : null,
33159 colYs : null, // array.
33166 bricks: null, //CompositeElement
33167 cols : 0, // array?
33168 // element : null, // wrapped now this.el
33169 _isLayoutInited : null,
33172 getAutoCreate : function(){
33176 cls: 'blog-masonary-wrapper ' + this.cls,
33178 cls : 'mas-boxes masonary'
33185 getChildContainer: function( )
33187 if (this.boxesEl) {
33188 return this.boxesEl;
33191 this.boxesEl = this.el.select('.mas-boxes').first();
33193 return this.boxesEl;
33197 initEvents : function()
33201 if(this.isAutoInitial){
33202 Roo.log('hook children rendered');
33203 this.on('childrenrendered', function() {
33204 Roo.log('children rendered');
33211 initial : function()
33213 this.reloadItems();
33215 this.currentSize = this.el.getBox(true);
33217 /// was window resize... - let's see if this works..
33218 Roo.EventManager.onWindowResize(this.resize, this);
33220 if(!this.isAutoInitial){
33225 this.layout.defer(500,this);
33228 reloadItems: function()
33230 this.bricks = this.el.select('.masonry-brick', true);
33232 this.bricks.each(function(b) {
33233 //Roo.log(b.getSize());
33234 if (!b.attr('originalwidth')) {
33235 b.attr('originalwidth', b.getSize().width);
33240 Roo.log(this.bricks.elements.length);
33243 resize : function()
33246 var cs = this.el.getBox(true);
33248 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33249 Roo.log("no change in with or X");
33252 this.currentSize = cs;
33256 layout : function()
33259 this._resetLayout();
33260 //this._manageStamps();
33262 // don't animate first layout
33263 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33264 this.layoutItems( isInstant );
33266 // flag for initalized
33267 this._isLayoutInited = true;
33270 layoutItems : function( isInstant )
33272 //var items = this._getItemsForLayout( this.items );
33273 // original code supports filtering layout items.. we just ignore it..
33275 this._layoutItems( this.bricks , isInstant );
33277 this._postLayout();
33279 _layoutItems : function ( items , isInstant)
33281 //this.fireEvent( 'layout', this, items );
33284 if ( !items || !items.elements.length ) {
33285 // no items, emit event with empty array
33290 items.each(function(item) {
33291 Roo.log("layout item");
33293 // get x/y object from method
33294 var position = this._getItemLayoutPosition( item );
33296 position.item = item;
33297 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33298 queue.push( position );
33301 this._processLayoutQueue( queue );
33303 /** Sets position of item in DOM
33304 * @param {Element} item
33305 * @param {Number} x - horizontal position
33306 * @param {Number} y - vertical position
33307 * @param {Boolean} isInstant - disables transitions
33309 _processLayoutQueue : function( queue )
33311 for ( var i=0, len = queue.length; i < len; i++ ) {
33312 var obj = queue[i];
33313 obj.item.position('absolute');
33314 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33320 * Any logic you want to do after each layout,
33321 * i.e. size the container
33323 _postLayout : function()
33325 this.resizeContainer();
33328 resizeContainer : function()
33330 if ( !this.isResizingContainer ) {
33333 var size = this._getContainerSize();
33335 this.el.setSize(size.width,size.height);
33336 this.boxesEl.setSize(size.width,size.height);
33342 _resetLayout : function()
33344 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33345 this.colWidth = this.el.getWidth();
33346 //this.gutter = this.el.getWidth();
33348 this.measureColumns();
33354 this.colYs.push( 0 );
33360 measureColumns : function()
33362 this.getContainerWidth();
33363 // if columnWidth is 0, default to outerWidth of first item
33364 if ( !this.columnWidth ) {
33365 var firstItem = this.bricks.first();
33366 Roo.log(firstItem);
33367 this.columnWidth = this.containerWidth;
33368 if (firstItem && firstItem.attr('originalwidth') ) {
33369 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33371 // columnWidth fall back to item of first element
33372 Roo.log("set column width?");
33373 this.initialColumnWidth = this.columnWidth ;
33375 // if first elem has no width, default to size of container
33380 if (this.initialColumnWidth) {
33381 this.columnWidth = this.initialColumnWidth;
33386 // column width is fixed at the top - however if container width get's smaller we should
33389 // this bit calcs how man columns..
33391 var columnWidth = this.columnWidth += this.gutter;
33393 // calculate columns
33394 var containerWidth = this.containerWidth + this.gutter;
33396 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33397 // fix rounding errors, typically with gutters
33398 var excess = columnWidth - containerWidth % columnWidth;
33401 // if overshoot is less than a pixel, round up, otherwise floor it
33402 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33403 cols = Math[ mathMethod ]( cols );
33404 this.cols = Math.max( cols, 1 );
33405 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33407 // padding positioning..
33408 var totalColWidth = this.cols * this.columnWidth;
33409 var padavail = this.containerWidth - totalColWidth;
33410 // so for 2 columns - we need 3 'pads'
33412 var padNeeded = (1+this.cols) * this.padWidth;
33414 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33416 this.columnWidth += padExtra
33417 //this.padWidth = Math.floor(padavail / ( this.cols));
33419 // adjust colum width so that padding is fixed??
33421 // we have 3 columns ... total = width * 3
33422 // we have X left over... that should be used by
33424 //if (this.expandC) {
33432 getContainerWidth : function()
33434 /* // container is parent if fit width
33435 var container = this.isFitWidth ? this.element.parentNode : this.element;
33436 // check that this.size and size are there
33437 // IE8 triggers resize on body size change, so they might not be
33439 var size = getSize( container ); //FIXME
33440 this.containerWidth = size && size.innerWidth; //FIXME
33443 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33447 _getItemLayoutPosition : function( item ) // what is item?
33449 // we resize the item to our columnWidth..
33451 item.setWidth(this.columnWidth);
33452 item.autoBoxAdjust = false;
33454 var sz = item.getSize();
33456 // how many columns does this brick span
33457 var remainder = this.containerWidth % this.columnWidth;
33459 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33460 // round if off by 1 pixel, otherwise use ceil
33461 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33462 colSpan = Math.min( colSpan, this.cols );
33464 // normally this should be '1' as we dont' currently allow multi width columns..
33466 var colGroup = this._getColGroup( colSpan );
33467 // get the minimum Y value from the columns
33468 var minimumY = Math.min.apply( Math, colGroup );
33469 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33471 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33473 // position the brick
33475 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33476 y: this.currentSize.y + minimumY + this.padHeight
33480 // apply setHeight to necessary columns
33481 var setHeight = minimumY + sz.height + this.padHeight;
33482 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33484 var setSpan = this.cols + 1 - colGroup.length;
33485 for ( var i = 0; i < setSpan; i++ ) {
33486 this.colYs[ shortColIndex + i ] = setHeight ;
33493 * @param {Number} colSpan - number of columns the element spans
33494 * @returns {Array} colGroup
33496 _getColGroup : function( colSpan )
33498 if ( colSpan < 2 ) {
33499 // if brick spans only one column, use all the column Ys
33504 // how many different places could this brick fit horizontally
33505 var groupCount = this.cols + 1 - colSpan;
33506 // for each group potential horizontal position
33507 for ( var i = 0; i < groupCount; i++ ) {
33508 // make an array of colY values for that one group
33509 var groupColYs = this.colYs.slice( i, i + colSpan );
33510 // and get the max value of the array
33511 colGroup[i] = Math.max.apply( Math, groupColYs );
33516 _manageStamp : function( stamp )
33518 var stampSize = stamp.getSize();
33519 var offset = stamp.getBox();
33520 // get the columns that this stamp affects
33521 var firstX = this.isOriginLeft ? offset.x : offset.right;
33522 var lastX = firstX + stampSize.width;
33523 var firstCol = Math.floor( firstX / this.columnWidth );
33524 firstCol = Math.max( 0, firstCol );
33526 var lastCol = Math.floor( lastX / this.columnWidth );
33527 // lastCol should not go over if multiple of columnWidth #425
33528 lastCol -= lastX % this.columnWidth ? 0 : 1;
33529 lastCol = Math.min( this.cols - 1, lastCol );
33531 // set colYs to bottom of the stamp
33532 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33535 for ( var i = firstCol; i <= lastCol; i++ ) {
33536 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33541 _getContainerSize : function()
33543 this.maxY = Math.max.apply( Math, this.colYs );
33548 if ( this.isFitWidth ) {
33549 size.width = this._getContainerFitWidth();
33555 _getContainerFitWidth : function()
33557 var unusedCols = 0;
33558 // count unused columns
33561 if ( this.colYs[i] !== 0 ) {
33566 // fit container to columns that have been used
33567 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33570 needsResizeLayout : function()
33572 var previousWidth = this.containerWidth;
33573 this.getContainerWidth();
33574 return previousWidth !== this.containerWidth;
33589 * @class Roo.bootstrap.MasonryBrick
33590 * @extends Roo.bootstrap.Component
33591 * Bootstrap MasonryBrick class
33594 * Create a new MasonryBrick
33595 * @param {Object} config The config object
33598 Roo.bootstrap.MasonryBrick = function(config){
33600 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33602 Roo.bootstrap.MasonryBrick.register(this);
33608 * When a MasonryBrick is clcik
33609 * @param {Roo.bootstrap.MasonryBrick} this
33610 * @param {Roo.EventObject} e
33616 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33619 * @cfg {String} title
33623 * @cfg {String} html
33627 * @cfg {String} bgimage
33631 * @cfg {String} videourl
33635 * @cfg {String} cls
33639 * @cfg {String} href
33643 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33648 * @cfg {String} placetitle (center|bottom)
33653 * @cfg {Boolean} isFitContainer defalut true
33655 isFitContainer : true,
33658 * @cfg {Boolean} preventDefault defalut false
33660 preventDefault : false,
33663 * @cfg {Boolean} inverse defalut false
33665 maskInverse : false,
33667 getAutoCreate : function()
33669 if(!this.isFitContainer){
33670 return this.getSplitAutoCreate();
33673 var cls = 'masonry-brick masonry-brick-full';
33675 if(this.href.length){
33676 cls += ' masonry-brick-link';
33679 if(this.bgimage.length){
33680 cls += ' masonry-brick-image';
33683 if(this.maskInverse){
33684 cls += ' mask-inverse';
33687 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33688 cls += ' enable-mask';
33692 cls += ' masonry-' + this.size + '-brick';
33695 if(this.placetitle.length){
33697 switch (this.placetitle) {
33699 cls += ' masonry-center-title';
33702 cls += ' masonry-bottom-title';
33709 if(!this.html.length && !this.bgimage.length){
33710 cls += ' masonry-center-title';
33713 if(!this.html.length && this.bgimage.length){
33714 cls += ' masonry-bottom-title';
33719 cls += ' ' + this.cls;
33723 tag: (this.href.length) ? 'a' : 'div',
33728 cls: 'masonry-brick-mask'
33732 cls: 'masonry-brick-paragraph',
33738 if(this.href.length){
33739 cfg.href = this.href;
33742 var cn = cfg.cn[1].cn;
33744 if(this.title.length){
33747 cls: 'masonry-brick-title',
33752 if(this.html.length){
33755 cls: 'masonry-brick-text',
33760 if (!this.title.length && !this.html.length) {
33761 cfg.cn[1].cls += ' hide';
33764 if(this.bgimage.length){
33767 cls: 'masonry-brick-image-view',
33772 if(this.videourl.length){
33773 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33774 // youtube support only?
33777 cls: 'masonry-brick-image-view',
33780 allowfullscreen : true
33788 getSplitAutoCreate : function()
33790 var cls = 'masonry-brick masonry-brick-split';
33792 if(this.href.length){
33793 cls += ' masonry-brick-link';
33796 if(this.bgimage.length){
33797 cls += ' masonry-brick-image';
33801 cls += ' masonry-' + this.size + '-brick';
33804 switch (this.placetitle) {
33806 cls += ' masonry-center-title';
33809 cls += ' masonry-bottom-title';
33812 if(!this.bgimage.length){
33813 cls += ' masonry-center-title';
33816 if(this.bgimage.length){
33817 cls += ' masonry-bottom-title';
33823 cls += ' ' + this.cls;
33827 tag: (this.href.length) ? 'a' : 'div',
33832 cls: 'masonry-brick-split-head',
33836 cls: 'masonry-brick-paragraph',
33843 cls: 'masonry-brick-split-body',
33849 if(this.href.length){
33850 cfg.href = this.href;
33853 if(this.title.length){
33854 cfg.cn[0].cn[0].cn.push({
33856 cls: 'masonry-brick-title',
33861 if(this.html.length){
33862 cfg.cn[1].cn.push({
33864 cls: 'masonry-brick-text',
33869 if(this.bgimage.length){
33870 cfg.cn[0].cn.push({
33872 cls: 'masonry-brick-image-view',
33877 if(this.videourl.length){
33878 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33879 // youtube support only?
33880 cfg.cn[0].cn.cn.push({
33882 cls: 'masonry-brick-image-view',
33885 allowfullscreen : true
33892 initEvents: function()
33894 switch (this.size) {
33927 this.el.on('touchstart', this.onTouchStart, this);
33928 this.el.on('touchmove', this.onTouchMove, this);
33929 this.el.on('touchend', this.onTouchEnd, this);
33930 this.el.on('contextmenu', this.onContextMenu, this);
33932 this.el.on('mouseenter' ,this.enter, this);
33933 this.el.on('mouseleave', this.leave, this);
33934 this.el.on('click', this.onClick, this);
33937 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33938 this.parent().bricks.push(this);
33943 onClick: function(e, el)
33945 var time = this.endTimer - this.startTimer;
33946 // Roo.log(e.preventDefault());
33949 e.preventDefault();
33954 if(!this.preventDefault){
33958 e.preventDefault();
33960 if (this.activeClass != '') {
33961 this.selectBrick();
33964 this.fireEvent('click', this, e);
33967 enter: function(e, el)
33969 e.preventDefault();
33971 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33975 if(this.bgimage.length && this.html.length){
33976 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33980 leave: function(e, el)
33982 e.preventDefault();
33984 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33988 if(this.bgimage.length && this.html.length){
33989 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33993 onTouchStart: function(e, el)
33995 // e.preventDefault();
33997 this.touchmoved = false;
33999 if(!this.isFitContainer){
34003 if(!this.bgimage.length || !this.html.length){
34007 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34009 this.timer = new Date().getTime();
34013 onTouchMove: function(e, el)
34015 this.touchmoved = true;
34018 onContextMenu : function(e,el)
34020 e.preventDefault();
34021 e.stopPropagation();
34025 onTouchEnd: function(e, el)
34027 // e.preventDefault();
34029 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34036 if(!this.bgimage.length || !this.html.length){
34038 if(this.href.length){
34039 window.location.href = this.href;
34045 if(!this.isFitContainer){
34049 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34051 window.location.href = this.href;
34054 //selection on single brick only
34055 selectBrick : function() {
34057 if (!this.parentId) {
34061 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34062 var index = m.selectedBrick.indexOf(this.id);
34065 m.selectedBrick.splice(index,1);
34066 this.el.removeClass(this.activeClass);
34070 for(var i = 0; i < m.selectedBrick.length; i++) {
34071 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34072 b.el.removeClass(b.activeClass);
34075 m.selectedBrick = [];
34077 m.selectedBrick.push(this.id);
34078 this.el.addClass(this.activeClass);
34082 isSelected : function(){
34083 return this.el.hasClass(this.activeClass);
34088 Roo.apply(Roo.bootstrap.MasonryBrick, {
34091 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34093 * register a Masonry Brick
34094 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34097 register : function(brick)
34099 //this.groups[brick.id] = brick;
34100 this.groups.add(brick.id, brick);
34103 * fetch a masonry brick based on the masonry brick ID
34104 * @param {string} the masonry brick to add
34105 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34108 get: function(brick_id)
34110 // if (typeof(this.groups[brick_id]) == 'undefined') {
34113 // return this.groups[brick_id] ;
34115 if(this.groups.key(brick_id)) {
34116 return this.groups.key(brick_id);
34134 * @class Roo.bootstrap.Brick
34135 * @extends Roo.bootstrap.Component
34136 * Bootstrap Brick class
34139 * Create a new Brick
34140 * @param {Object} config The config object
34143 Roo.bootstrap.Brick = function(config){
34144 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34150 * When a Brick is click
34151 * @param {Roo.bootstrap.Brick} this
34152 * @param {Roo.EventObject} e
34158 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
34161 * @cfg {String} title
34165 * @cfg {String} html
34169 * @cfg {String} bgimage
34173 * @cfg {String} cls
34177 * @cfg {String} href
34181 * @cfg {String} video
34185 * @cfg {Boolean} square
34189 getAutoCreate : function()
34191 var cls = 'roo-brick';
34193 if(this.href.length){
34194 cls += ' roo-brick-link';
34197 if(this.bgimage.length){
34198 cls += ' roo-brick-image';
34201 if(!this.html.length && !this.bgimage.length){
34202 cls += ' roo-brick-center-title';
34205 if(!this.html.length && this.bgimage.length){
34206 cls += ' roo-brick-bottom-title';
34210 cls += ' ' + this.cls;
34214 tag: (this.href.length) ? 'a' : 'div',
34219 cls: 'roo-brick-paragraph',
34225 if(this.href.length){
34226 cfg.href = this.href;
34229 var cn = cfg.cn[0].cn;
34231 if(this.title.length){
34234 cls: 'roo-brick-title',
34239 if(this.html.length){
34242 cls: 'roo-brick-text',
34249 if(this.bgimage.length){
34252 cls: 'roo-brick-image-view',
34260 initEvents: function()
34262 if(this.title.length || this.html.length){
34263 this.el.on('mouseenter' ,this.enter, this);
34264 this.el.on('mouseleave', this.leave, this);
34267 Roo.EventManager.onWindowResize(this.resize, this);
34269 if(this.bgimage.length){
34270 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34271 this.imageEl.on('load', this.onImageLoad, this);
34278 onImageLoad : function()
34283 resize : function()
34285 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34287 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34289 if(this.bgimage.length){
34290 var image = this.el.select('.roo-brick-image-view', true).first();
34292 image.setWidth(paragraph.getWidth());
34295 image.setHeight(paragraph.getWidth());
34298 this.el.setHeight(image.getHeight());
34299 paragraph.setHeight(image.getHeight());
34305 enter: function(e, el)
34307 e.preventDefault();
34309 if(this.bgimage.length){
34310 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34311 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34315 leave: function(e, el)
34317 e.preventDefault();
34319 if(this.bgimage.length){
34320 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34321 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34336 * @class Roo.bootstrap.NumberField
34337 * @extends Roo.bootstrap.Input
34338 * Bootstrap NumberField class
34344 * Create a new NumberField
34345 * @param {Object} config The config object
34348 Roo.bootstrap.NumberField = function(config){
34349 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34352 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34355 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34357 allowDecimals : true,
34359 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34361 decimalSeparator : ".",
34363 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34365 decimalPrecision : 2,
34367 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34369 allowNegative : true,
34372 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34376 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34378 minValue : Number.NEGATIVE_INFINITY,
34380 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34382 maxValue : Number.MAX_VALUE,
34384 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34386 minText : "The minimum value for this field is {0}",
34388 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34390 maxText : "The maximum value for this field is {0}",
34392 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34393 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34395 nanText : "{0} is not a valid number",
34397 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34399 thousandsDelimiter : false,
34401 * @cfg {String} valueAlign alignment of value
34403 valueAlign : "left",
34405 getAutoCreate : function()
34407 var hiddenInput = {
34411 cls: 'hidden-number-input'
34415 hiddenInput.name = this.name;
34420 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34422 this.name = hiddenInput.name;
34424 if(cfg.cn.length > 0) {
34425 cfg.cn.push(hiddenInput);
34432 initEvents : function()
34434 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34436 var allowed = "0123456789";
34438 if(this.allowDecimals){
34439 allowed += this.decimalSeparator;
34442 if(this.allowNegative){
34446 if(this.thousandsDelimiter) {
34450 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34452 var keyPress = function(e){
34454 var k = e.getKey();
34456 var c = e.getCharCode();
34459 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34460 allowed.indexOf(String.fromCharCode(c)) === -1
34466 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34470 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34475 this.el.on("keypress", keyPress, this);
34478 validateValue : function(value)
34481 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34485 var num = this.parseValue(value);
34488 this.markInvalid(String.format(this.nanText, value));
34492 if(num < this.minValue){
34493 this.markInvalid(String.format(this.minText, this.minValue));
34497 if(num > this.maxValue){
34498 this.markInvalid(String.format(this.maxText, this.maxValue));
34505 getValue : function()
34507 var v = this.hiddenEl().getValue();
34509 return this.fixPrecision(this.parseValue(v));
34512 parseValue : function(value)
34514 if(this.thousandsDelimiter) {
34516 r = new RegExp(",", "g");
34517 value = value.replace(r, "");
34520 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34521 return isNaN(value) ? '' : value;
34524 fixPrecision : function(value)
34526 if(this.thousandsDelimiter) {
34528 r = new RegExp(",", "g");
34529 value = value.replace(r, "");
34532 var nan = isNaN(value);
34534 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34535 return nan ? '' : value;
34537 return parseFloat(value).toFixed(this.decimalPrecision);
34540 setValue : function(v)
34542 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34548 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34550 this.inputEl().dom.value = (v == '') ? '' :
34551 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34553 if(!this.allowZero && v === '0') {
34554 this.hiddenEl().dom.value = '';
34555 this.inputEl().dom.value = '';
34562 decimalPrecisionFcn : function(v)
34564 return Math.floor(v);
34567 beforeBlur : function()
34569 var v = this.parseValue(this.getRawValue());
34571 if(v || v === 0 || v === ''){
34576 hiddenEl : function()
34578 return this.el.select('input.hidden-number-input',true).first();
34590 * @class Roo.bootstrap.DocumentSlider
34591 * @extends Roo.bootstrap.Component
34592 * Bootstrap DocumentSlider class
34595 * Create a new DocumentViewer
34596 * @param {Object} config The config object
34599 Roo.bootstrap.DocumentSlider = function(config){
34600 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34607 * Fire after initEvent
34608 * @param {Roo.bootstrap.DocumentSlider} this
34613 * Fire after update
34614 * @param {Roo.bootstrap.DocumentSlider} this
34620 * @param {Roo.bootstrap.DocumentSlider} this
34626 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34632 getAutoCreate : function()
34636 cls : 'roo-document-slider',
34640 cls : 'roo-document-slider-header',
34644 cls : 'roo-document-slider-header-title'
34650 cls : 'roo-document-slider-body',
34654 cls : 'roo-document-slider-prev',
34658 cls : 'fa fa-chevron-left'
34664 cls : 'roo-document-slider-thumb',
34668 cls : 'roo-document-slider-image'
34674 cls : 'roo-document-slider-next',
34678 cls : 'fa fa-chevron-right'
34690 initEvents : function()
34692 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34693 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34695 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34696 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34698 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34699 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34701 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34702 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34704 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34705 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34707 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34708 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34710 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34711 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34713 this.thumbEl.on('click', this.onClick, this);
34715 this.prevIndicator.on('click', this.prev, this);
34717 this.nextIndicator.on('click', this.next, this);
34721 initial : function()
34723 if(this.files.length){
34724 this.indicator = 1;
34728 this.fireEvent('initial', this);
34731 update : function()
34733 this.imageEl.attr('src', this.files[this.indicator - 1]);
34735 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34737 this.prevIndicator.show();
34739 if(this.indicator == 1){
34740 this.prevIndicator.hide();
34743 this.nextIndicator.show();
34745 if(this.indicator == this.files.length){
34746 this.nextIndicator.hide();
34749 this.thumbEl.scrollTo('top');
34751 this.fireEvent('update', this);
34754 onClick : function(e)
34756 e.preventDefault();
34758 this.fireEvent('click', this);
34763 e.preventDefault();
34765 this.indicator = Math.max(1, this.indicator - 1);
34772 e.preventDefault();
34774 this.indicator = Math.min(this.files.length, this.indicator + 1);
34788 * @class Roo.bootstrap.RadioSet
34789 * @extends Roo.bootstrap.Input
34790 * Bootstrap RadioSet class
34791 * @cfg {String} indicatorpos (left|right) default left
34792 * @cfg {Boolean} inline (true|false) inline the element (default true)
34793 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34795 * Create a new RadioSet
34796 * @param {Object} config The config object
34799 Roo.bootstrap.RadioSet = function(config){
34801 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34805 Roo.bootstrap.RadioSet.register(this);
34810 * Fires when the element is checked or unchecked.
34811 * @param {Roo.bootstrap.RadioSet} this This radio
34812 * @param {Roo.bootstrap.Radio} item The checked item
34817 * Fires when the element is click.
34818 * @param {Roo.bootstrap.RadioSet} this This radio set
34819 * @param {Roo.bootstrap.Radio} item The checked item
34820 * @param {Roo.EventObject} e The event object
34827 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34835 indicatorpos : 'left',
34837 getAutoCreate : function()
34841 cls : 'roo-radio-set-label',
34845 html : this.fieldLabel
34849 if (Roo.bootstrap.version == 3) {
34852 if(this.indicatorpos == 'left'){
34855 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34856 tooltip : 'This field is required'
34861 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34862 tooltip : 'This field is required'
34868 cls : 'roo-radio-set-items'
34871 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34873 if (align === 'left' && this.fieldLabel.length) {
34876 cls : "roo-radio-set-right",
34882 if(this.labelWidth > 12){
34883 label.style = "width: " + this.labelWidth + 'px';
34886 if(this.labelWidth < 13 && this.labelmd == 0){
34887 this.labelmd = this.labelWidth;
34890 if(this.labellg > 0){
34891 label.cls += ' col-lg-' + this.labellg;
34892 items.cls += ' col-lg-' + (12 - this.labellg);
34895 if(this.labelmd > 0){
34896 label.cls += ' col-md-' + this.labelmd;
34897 items.cls += ' col-md-' + (12 - this.labelmd);
34900 if(this.labelsm > 0){
34901 label.cls += ' col-sm-' + this.labelsm;
34902 items.cls += ' col-sm-' + (12 - this.labelsm);
34905 if(this.labelxs > 0){
34906 label.cls += ' col-xs-' + this.labelxs;
34907 items.cls += ' col-xs-' + (12 - this.labelxs);
34913 cls : 'roo-radio-set',
34917 cls : 'roo-radio-set-input',
34920 value : this.value ? this.value : ''
34927 if(this.weight.length){
34928 cfg.cls += ' roo-radio-' + this.weight;
34932 cfg.cls += ' roo-radio-set-inline';
34936 ['xs','sm','md','lg'].map(function(size){
34937 if (settings[size]) {
34938 cfg.cls += ' col-' + size + '-' + settings[size];
34946 initEvents : function()
34948 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34949 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34951 if(!this.fieldLabel.length){
34952 this.labelEl.hide();
34955 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34956 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34958 this.indicator = this.indicatorEl();
34960 if(this.indicator){
34961 this.indicator.addClass('invisible');
34964 this.originalValue = this.getValue();
34968 inputEl: function ()
34970 return this.el.select('.roo-radio-set-input', true).first();
34973 getChildContainer : function()
34975 return this.itemsEl;
34978 register : function(item)
34980 this.radioes.push(item);
34984 validate : function()
34986 if(this.getVisibilityEl().hasClass('hidden')){
34992 Roo.each(this.radioes, function(i){
35001 if(this.allowBlank) {
35005 if(this.disabled || valid){
35010 this.markInvalid();
35015 markValid : function()
35017 if(this.labelEl.isVisible(true) && this.indicatorEl()){
35018 this.indicatorEl().removeClass('visible');
35019 this.indicatorEl().addClass('invisible');
35023 if (Roo.bootstrap.version == 3) {
35024 this.el.removeClass([this.invalidClass, this.validClass]);
35025 this.el.addClass(this.validClass);
35027 this.el.removeClass(['is-invalid','is-valid']);
35028 this.el.addClass(['is-valid']);
35030 this.fireEvent('valid', this);
35033 markInvalid : function(msg)
35035 if(this.allowBlank || this.disabled){
35039 if(this.labelEl.isVisible(true) && this.indicatorEl()){
35040 this.indicatorEl().removeClass('invisible');
35041 this.indicatorEl().addClass('visible');
35043 if (Roo.bootstrap.version == 3) {
35044 this.el.removeClass([this.invalidClass, this.validClass]);
35045 this.el.addClass(this.invalidClass);
35047 this.el.removeClass(['is-invalid','is-valid']);
35048 this.el.addClass(['is-invalid']);
35051 this.fireEvent('invalid', this, msg);
35055 setValue : function(v, suppressEvent)
35057 if(this.value === v){
35064 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35067 Roo.each(this.radioes, function(i){
35069 i.el.removeClass('checked');
35072 Roo.each(this.radioes, function(i){
35074 if(i.value === v || i.value.toString() === v.toString()){
35076 i.el.addClass('checked');
35078 if(suppressEvent !== true){
35079 this.fireEvent('check', this, i);
35090 clearInvalid : function(){
35092 if(!this.el || this.preventMark){
35096 this.el.removeClass([this.invalidClass]);
35098 this.fireEvent('valid', this);
35103 Roo.apply(Roo.bootstrap.RadioSet, {
35107 register : function(set)
35109 this.groups[set.name] = set;
35112 get: function(name)
35114 if (typeof(this.groups[name]) == 'undefined') {
35118 return this.groups[name] ;
35124 * Ext JS Library 1.1.1
35125 * Copyright(c) 2006-2007, Ext JS, LLC.
35127 * Originally Released Under LGPL - original licence link has changed is not relivant.
35130 * <script type="text/javascript">
35135 * @class Roo.bootstrap.SplitBar
35136 * @extends Roo.util.Observable
35137 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35141 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35142 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35143 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35144 split.minSize = 100;
35145 split.maxSize = 600;
35146 split.animate = true;
35147 split.on('moved', splitterMoved);
35150 * Create a new SplitBar
35151 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
35152 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
35153 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35154 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
35155 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35156 position of the SplitBar).
35158 Roo.bootstrap.SplitBar = function(cfg){
35163 // dragElement : elm
35164 // resizingElement: el,
35166 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35167 // placement : Roo.bootstrap.SplitBar.LEFT ,
35168 // existingProxy ???
35171 this.el = Roo.get(cfg.dragElement, true);
35172 this.el.dom.unselectable = "on";
35174 this.resizingEl = Roo.get(cfg.resizingElement, true);
35178 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35179 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35182 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35185 * The minimum size of the resizing element. (Defaults to 0)
35191 * The maximum size of the resizing element. (Defaults to 2000)
35194 this.maxSize = 2000;
35197 * Whether to animate the transition to the new size
35200 this.animate = false;
35203 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35206 this.useShim = false;
35211 if(!cfg.existingProxy){
35213 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35215 this.proxy = Roo.get(cfg.existingProxy).dom;
35218 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35221 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35224 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35227 this.dragSpecs = {};
35230 * @private The adapter to use to positon and resize elements
35232 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35233 this.adapter.init(this);
35235 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35237 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35238 this.el.addClass("roo-splitbar-h");
35241 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35242 this.el.addClass("roo-splitbar-v");
35248 * Fires when the splitter is moved (alias for {@link #event-moved})
35249 * @param {Roo.bootstrap.SplitBar} this
35250 * @param {Number} newSize the new width or height
35255 * Fires when the splitter is moved
35256 * @param {Roo.bootstrap.SplitBar} this
35257 * @param {Number} newSize the new width or height
35261 * @event beforeresize
35262 * Fires before the splitter is dragged
35263 * @param {Roo.bootstrap.SplitBar} this
35265 "beforeresize" : true,
35267 "beforeapply" : true
35270 Roo.util.Observable.call(this);
35273 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35274 onStartProxyDrag : function(x, y){
35275 this.fireEvent("beforeresize", this);
35277 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35279 o.enableDisplayMode("block");
35280 // all splitbars share the same overlay
35281 Roo.bootstrap.SplitBar.prototype.overlay = o;
35283 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35284 this.overlay.show();
35285 Roo.get(this.proxy).setDisplayed("block");
35286 var size = this.adapter.getElementSize(this);
35287 this.activeMinSize = this.getMinimumSize();;
35288 this.activeMaxSize = this.getMaximumSize();;
35289 var c1 = size - this.activeMinSize;
35290 var c2 = Math.max(this.activeMaxSize - size, 0);
35291 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35292 this.dd.resetConstraints();
35293 this.dd.setXConstraint(
35294 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35295 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35297 this.dd.setYConstraint(0, 0);
35299 this.dd.resetConstraints();
35300 this.dd.setXConstraint(0, 0);
35301 this.dd.setYConstraint(
35302 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35303 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35306 this.dragSpecs.startSize = size;
35307 this.dragSpecs.startPoint = [x, y];
35308 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35312 * @private Called after the drag operation by the DDProxy
35314 onEndProxyDrag : function(e){
35315 Roo.get(this.proxy).setDisplayed(false);
35316 var endPoint = Roo.lib.Event.getXY(e);
35318 this.overlay.hide();
35321 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35322 newSize = this.dragSpecs.startSize +
35323 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35324 endPoint[0] - this.dragSpecs.startPoint[0] :
35325 this.dragSpecs.startPoint[0] - endPoint[0]
35328 newSize = this.dragSpecs.startSize +
35329 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35330 endPoint[1] - this.dragSpecs.startPoint[1] :
35331 this.dragSpecs.startPoint[1] - endPoint[1]
35334 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35335 if(newSize != this.dragSpecs.startSize){
35336 if(this.fireEvent('beforeapply', this, newSize) !== false){
35337 this.adapter.setElementSize(this, newSize);
35338 this.fireEvent("moved", this, newSize);
35339 this.fireEvent("resize", this, newSize);
35345 * Get the adapter this SplitBar uses
35346 * @return The adapter object
35348 getAdapter : function(){
35349 return this.adapter;
35353 * Set the adapter this SplitBar uses
35354 * @param {Object} adapter A SplitBar adapter object
35356 setAdapter : function(adapter){
35357 this.adapter = adapter;
35358 this.adapter.init(this);
35362 * Gets the minimum size for the resizing element
35363 * @return {Number} The minimum size
35365 getMinimumSize : function(){
35366 return this.minSize;
35370 * Sets the minimum size for the resizing element
35371 * @param {Number} minSize The minimum size
35373 setMinimumSize : function(minSize){
35374 this.minSize = minSize;
35378 * Gets the maximum size for the resizing element
35379 * @return {Number} The maximum size
35381 getMaximumSize : function(){
35382 return this.maxSize;
35386 * Sets the maximum size for the resizing element
35387 * @param {Number} maxSize The maximum size
35389 setMaximumSize : function(maxSize){
35390 this.maxSize = maxSize;
35394 * Sets the initialize size for the resizing element
35395 * @param {Number} size The initial size
35397 setCurrentSize : function(size){
35398 var oldAnimate = this.animate;
35399 this.animate = false;
35400 this.adapter.setElementSize(this, size);
35401 this.animate = oldAnimate;
35405 * Destroy this splitbar.
35406 * @param {Boolean} removeEl True to remove the element
35408 destroy : function(removeEl){
35410 this.shim.remove();
35413 this.proxy.parentNode.removeChild(this.proxy);
35421 * @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.
35423 Roo.bootstrap.SplitBar.createProxy = function(dir){
35424 var proxy = new Roo.Element(document.createElement("div"));
35425 proxy.unselectable();
35426 var cls = 'roo-splitbar-proxy';
35427 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35428 document.body.appendChild(proxy.dom);
35433 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35434 * Default Adapter. It assumes the splitter and resizing element are not positioned
35435 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35437 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35440 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35441 // do nothing for now
35442 init : function(s){
35446 * Called before drag operations to get the current size of the resizing element.
35447 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35449 getElementSize : function(s){
35450 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35451 return s.resizingEl.getWidth();
35453 return s.resizingEl.getHeight();
35458 * Called after drag operations to set the size of the resizing element.
35459 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35460 * @param {Number} newSize The new size to set
35461 * @param {Function} onComplete A function to be invoked when resizing is complete
35463 setElementSize : function(s, newSize, onComplete){
35464 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35466 s.resizingEl.setWidth(newSize);
35468 onComplete(s, newSize);
35471 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35476 s.resizingEl.setHeight(newSize);
35478 onComplete(s, newSize);
35481 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35488 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35489 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35490 * Adapter that moves the splitter element to align with the resized sizing element.
35491 * Used with an absolute positioned SplitBar.
35492 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35493 * document.body, make sure you assign an id to the body element.
35495 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35496 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35497 this.container = Roo.get(container);
35500 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35501 init : function(s){
35502 this.basic.init(s);
35505 getElementSize : function(s){
35506 return this.basic.getElementSize(s);
35509 setElementSize : function(s, newSize, onComplete){
35510 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35513 moveSplitter : function(s){
35514 var yes = Roo.bootstrap.SplitBar;
35515 switch(s.placement){
35517 s.el.setX(s.resizingEl.getRight());
35520 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35523 s.el.setY(s.resizingEl.getBottom());
35526 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35533 * Orientation constant - Create a vertical SplitBar
35537 Roo.bootstrap.SplitBar.VERTICAL = 1;
35540 * Orientation constant - Create a horizontal SplitBar
35544 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35547 * Placement constant - The resizing element is to the left of the splitter element
35551 Roo.bootstrap.SplitBar.LEFT = 1;
35554 * Placement constant - The resizing element is to the right of the splitter element
35558 Roo.bootstrap.SplitBar.RIGHT = 2;
35561 * Placement constant - The resizing element is positioned above the splitter element
35565 Roo.bootstrap.SplitBar.TOP = 3;
35568 * Placement constant - The resizing element is positioned under splitter element
35572 Roo.bootstrap.SplitBar.BOTTOM = 4;
35573 Roo.namespace("Roo.bootstrap.layout");/*
35575 * Ext JS Library 1.1.1
35576 * Copyright(c) 2006-2007, Ext JS, LLC.
35578 * Originally Released Under LGPL - original licence link has changed is not relivant.
35581 * <script type="text/javascript">
35585 * @class Roo.bootstrap.layout.Manager
35586 * @extends Roo.bootstrap.Component
35587 * Base class for layout managers.
35589 Roo.bootstrap.layout.Manager = function(config)
35591 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35597 /** false to disable window resize monitoring @type Boolean */
35598 this.monitorWindowResize = true;
35603 * Fires when a layout is performed.
35604 * @param {Roo.LayoutManager} this
35608 * @event regionresized
35609 * Fires when the user resizes a region.
35610 * @param {Roo.LayoutRegion} region The resized region
35611 * @param {Number} newSize The new size (width for east/west, height for north/south)
35613 "regionresized" : true,
35615 * @event regioncollapsed
35616 * Fires when a region is collapsed.
35617 * @param {Roo.LayoutRegion} region The collapsed region
35619 "regioncollapsed" : true,
35621 * @event regionexpanded
35622 * Fires when a region is expanded.
35623 * @param {Roo.LayoutRegion} region The expanded region
35625 "regionexpanded" : true
35627 this.updating = false;
35630 this.el = Roo.get(config.el);
35636 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35641 monitorWindowResize : true,
35647 onRender : function(ct, position)
35650 this.el = Roo.get(ct);
35653 //this.fireEvent('render',this);
35657 initEvents: function()
35661 // ie scrollbar fix
35662 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35663 document.body.scroll = "no";
35664 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35665 this.el.position('relative');
35667 this.id = this.el.id;
35668 this.el.addClass("roo-layout-container");
35669 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35670 if(this.el.dom != document.body ) {
35671 this.el.on('resize', this.layout,this);
35672 this.el.on('show', this.layout,this);
35678 * Returns true if this layout is currently being updated
35679 * @return {Boolean}
35681 isUpdating : function(){
35682 return this.updating;
35686 * Suspend the LayoutManager from doing auto-layouts while
35687 * making multiple add or remove calls
35689 beginUpdate : function(){
35690 this.updating = true;
35694 * Restore auto-layouts and optionally disable the manager from performing a layout
35695 * @param {Boolean} noLayout true to disable a layout update
35697 endUpdate : function(noLayout){
35698 this.updating = false;
35704 layout: function(){
35708 onRegionResized : function(region, newSize){
35709 this.fireEvent("regionresized", region, newSize);
35713 onRegionCollapsed : function(region){
35714 this.fireEvent("regioncollapsed", region);
35717 onRegionExpanded : function(region){
35718 this.fireEvent("regionexpanded", region);
35722 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35723 * performs box-model adjustments.
35724 * @return {Object} The size as an object {width: (the width), height: (the height)}
35726 getViewSize : function()
35729 if(this.el.dom != document.body){
35730 size = this.el.getSize();
35732 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35734 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35735 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35740 * Returns the Element this layout is bound to.
35741 * @return {Roo.Element}
35743 getEl : function(){
35748 * Returns the specified region.
35749 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35750 * @return {Roo.LayoutRegion}
35752 getRegion : function(target){
35753 return this.regions[target.toLowerCase()];
35756 onWindowResize : function(){
35757 if(this.monitorWindowResize){
35764 * Ext JS Library 1.1.1
35765 * Copyright(c) 2006-2007, Ext JS, LLC.
35767 * Originally Released Under LGPL - original licence link has changed is not relivant.
35770 * <script type="text/javascript">
35773 * @class Roo.bootstrap.layout.Border
35774 * @extends Roo.bootstrap.layout.Manager
35775 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35776 * please see: examples/bootstrap/nested.html<br><br>
35778 <b>The container the layout is rendered into can be either the body element or any other element.
35779 If it is not the body element, the container needs to either be an absolute positioned element,
35780 or you will need to add "position:relative" to the css of the container. You will also need to specify
35781 the container size if it is not the body element.</b>
35784 * Create a new Border
35785 * @param {Object} config Configuration options
35787 Roo.bootstrap.layout.Border = function(config){
35788 config = config || {};
35789 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35793 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35794 if(config[region]){
35795 config[region].region = region;
35796 this.addRegion(config[region]);
35802 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35804 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35806 parent : false, // this might point to a 'nest' or a ???
35809 * Creates and adds a new region if it doesn't already exist.
35810 * @param {String} target The target region key (north, south, east, west or center).
35811 * @param {Object} config The regions config object
35812 * @return {BorderLayoutRegion} The new region
35814 addRegion : function(config)
35816 if(!this.regions[config.region]){
35817 var r = this.factory(config);
35818 this.bindRegion(r);
35820 return this.regions[config.region];
35824 bindRegion : function(r){
35825 this.regions[r.config.region] = r;
35827 r.on("visibilitychange", this.layout, this);
35828 r.on("paneladded", this.layout, this);
35829 r.on("panelremoved", this.layout, this);
35830 r.on("invalidated", this.layout, this);
35831 r.on("resized", this.onRegionResized, this);
35832 r.on("collapsed", this.onRegionCollapsed, this);
35833 r.on("expanded", this.onRegionExpanded, this);
35837 * Performs a layout update.
35839 layout : function()
35841 if(this.updating) {
35845 // render all the rebions if they have not been done alreayd?
35846 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35847 if(this.regions[region] && !this.regions[region].bodyEl){
35848 this.regions[region].onRender(this.el)
35852 var size = this.getViewSize();
35853 var w = size.width;
35854 var h = size.height;
35859 //var x = 0, y = 0;
35861 var rs = this.regions;
35862 var north = rs["north"];
35863 var south = rs["south"];
35864 var west = rs["west"];
35865 var east = rs["east"];
35866 var center = rs["center"];
35867 //if(this.hideOnLayout){ // not supported anymore
35868 //c.el.setStyle("display", "none");
35870 if(north && north.isVisible()){
35871 var b = north.getBox();
35872 var m = north.getMargins();
35873 b.width = w - (m.left+m.right);
35876 centerY = b.height + b.y + m.bottom;
35877 centerH -= centerY;
35878 north.updateBox(this.safeBox(b));
35880 if(south && south.isVisible()){
35881 var b = south.getBox();
35882 var m = south.getMargins();
35883 b.width = w - (m.left+m.right);
35885 var totalHeight = (b.height + m.top + m.bottom);
35886 b.y = h - totalHeight + m.top;
35887 centerH -= totalHeight;
35888 south.updateBox(this.safeBox(b));
35890 if(west && west.isVisible()){
35891 var b = west.getBox();
35892 var m = west.getMargins();
35893 b.height = centerH - (m.top+m.bottom);
35895 b.y = centerY + m.top;
35896 var totalWidth = (b.width + m.left + m.right);
35897 centerX += totalWidth;
35898 centerW -= totalWidth;
35899 west.updateBox(this.safeBox(b));
35901 if(east && east.isVisible()){
35902 var b = east.getBox();
35903 var m = east.getMargins();
35904 b.height = centerH - (m.top+m.bottom);
35905 var totalWidth = (b.width + m.left + m.right);
35906 b.x = w - totalWidth + m.left;
35907 b.y = centerY + m.top;
35908 centerW -= totalWidth;
35909 east.updateBox(this.safeBox(b));
35912 var m = center.getMargins();
35914 x: centerX + m.left,
35915 y: centerY + m.top,
35916 width: centerW - (m.left+m.right),
35917 height: centerH - (m.top+m.bottom)
35919 //if(this.hideOnLayout){
35920 //center.el.setStyle("display", "block");
35922 center.updateBox(this.safeBox(centerBox));
35925 this.fireEvent("layout", this);
35929 safeBox : function(box){
35930 box.width = Math.max(0, box.width);
35931 box.height = Math.max(0, box.height);
35936 * Adds a ContentPanel (or subclass) to this layout.
35937 * @param {String} target The target region key (north, south, east, west or center).
35938 * @param {Roo.ContentPanel} panel The panel to add
35939 * @return {Roo.ContentPanel} The added panel
35941 add : function(target, panel){
35943 target = target.toLowerCase();
35944 return this.regions[target].add(panel);
35948 * Remove a ContentPanel (or subclass) to this layout.
35949 * @param {String} target The target region key (north, south, east, west or center).
35950 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35951 * @return {Roo.ContentPanel} The removed panel
35953 remove : function(target, panel){
35954 target = target.toLowerCase();
35955 return this.regions[target].remove(panel);
35959 * Searches all regions for a panel with the specified id
35960 * @param {String} panelId
35961 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35963 findPanel : function(panelId){
35964 var rs = this.regions;
35965 for(var target in rs){
35966 if(typeof rs[target] != "function"){
35967 var p = rs[target].getPanel(panelId);
35977 * Searches all regions for a panel with the specified id and activates (shows) it.
35978 * @param {String/ContentPanel} panelId The panels id or the panel itself
35979 * @return {Roo.ContentPanel} The shown panel or null
35981 showPanel : function(panelId) {
35982 var rs = this.regions;
35983 for(var target in rs){
35984 var r = rs[target];
35985 if(typeof r != "function"){
35986 if(r.hasPanel(panelId)){
35987 return r.showPanel(panelId);
35995 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35996 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35999 restoreState : function(provider){
36001 provider = Roo.state.Manager;
36003 var sm = new Roo.LayoutStateManager();
36004 sm.init(this, provider);
36010 * Adds a xtype elements to the layout.
36014 xtype : 'ContentPanel',
36021 xtype : 'NestedLayoutPanel',
36027 items : [ ... list of content panels or nested layout panels.. ]
36031 * @param {Object} cfg Xtype definition of item to add.
36033 addxtype : function(cfg)
36035 // basically accepts a pannel...
36036 // can accept a layout region..!?!?
36037 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36040 // theory? children can only be panels??
36042 //if (!cfg.xtype.match(/Panel$/)) {
36047 if (typeof(cfg.region) == 'undefined') {
36048 Roo.log("Failed to add Panel, region was not set");
36052 var region = cfg.region;
36058 xitems = cfg.items;
36063 if ( region == 'center') {
36064 Roo.log("Center: " + cfg.title);
36070 case 'Content': // ContentPanel (el, cfg)
36071 case 'Scroll': // ContentPanel (el, cfg)
36073 cfg.autoCreate = cfg.autoCreate || true;
36074 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36076 // var el = this.el.createChild();
36077 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36080 this.add(region, ret);
36084 case 'TreePanel': // our new panel!
36085 cfg.el = this.el.createChild();
36086 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36087 this.add(region, ret);
36092 // create a new Layout (which is a Border Layout...
36094 var clayout = cfg.layout;
36095 clayout.el = this.el.createChild();
36096 clayout.items = clayout.items || [];
36100 // replace this exitems with the clayout ones..
36101 xitems = clayout.items;
36103 // force background off if it's in center...
36104 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36105 cfg.background = false;
36107 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
36110 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36111 //console.log('adding nested layout panel ' + cfg.toSource());
36112 this.add(region, ret);
36113 nb = {}; /// find first...
36118 // needs grid and region
36120 //var el = this.getRegion(region).el.createChild();
36122 *var el = this.el.createChild();
36123 // create the grid first...
36124 cfg.grid.container = el;
36125 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36128 if (region == 'center' && this.active ) {
36129 cfg.background = false;
36132 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36134 this.add(region, ret);
36136 if (cfg.background) {
36137 // render grid on panel activation (if panel background)
36138 ret.on('activate', function(gp) {
36139 if (!gp.grid.rendered) {
36140 // gp.grid.render(el);
36144 // cfg.grid.render(el);
36150 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36151 // it was the old xcomponent building that caused this before.
36152 // espeically if border is the top element in the tree.
36162 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36164 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36165 this.add(region, ret);
36169 throw "Can not add '" + cfg.xtype + "' to Border";
36175 this.beginUpdate();
36179 Roo.each(xitems, function(i) {
36180 region = nb && i.region ? i.region : false;
36182 var add = ret.addxtype(i);
36185 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36186 if (!i.background) {
36187 abn[region] = nb[region] ;
36194 // make the last non-background panel active..
36195 //if (nb) { Roo.log(abn); }
36198 for(var r in abn) {
36199 region = this.getRegion(r);
36201 // tried using nb[r], but it does not work..
36203 region.showPanel(abn[r]);
36214 factory : function(cfg)
36217 var validRegions = Roo.bootstrap.layout.Border.regions;
36219 var target = cfg.region;
36222 var r = Roo.bootstrap.layout;
36226 return new r.North(cfg);
36228 return new r.South(cfg);
36230 return new r.East(cfg);
36232 return new r.West(cfg);
36234 return new r.Center(cfg);
36236 throw 'Layout region "'+target+'" not supported.';
36243 * Ext JS Library 1.1.1
36244 * Copyright(c) 2006-2007, Ext JS, LLC.
36246 * Originally Released Under LGPL - original licence link has changed is not relivant.
36249 * <script type="text/javascript">
36253 * @class Roo.bootstrap.layout.Basic
36254 * @extends Roo.util.Observable
36255 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36256 * and does not have a titlebar, tabs or any other features. All it does is size and position
36257 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36258 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36259 * @cfg {string} region the region that it inhabits..
36260 * @cfg {bool} skipConfig skip config?
36264 Roo.bootstrap.layout.Basic = function(config){
36266 this.mgr = config.mgr;
36268 this.position = config.region;
36270 var skipConfig = config.skipConfig;
36274 * @scope Roo.BasicLayoutRegion
36278 * @event beforeremove
36279 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36280 * @param {Roo.LayoutRegion} this
36281 * @param {Roo.ContentPanel} panel The panel
36282 * @param {Object} e The cancel event object
36284 "beforeremove" : true,
36286 * @event invalidated
36287 * Fires when the layout for this region is changed.
36288 * @param {Roo.LayoutRegion} this
36290 "invalidated" : true,
36292 * @event visibilitychange
36293 * Fires when this region is shown or hidden
36294 * @param {Roo.LayoutRegion} this
36295 * @param {Boolean} visibility true or false
36297 "visibilitychange" : true,
36299 * @event paneladded
36300 * Fires when a panel is added.
36301 * @param {Roo.LayoutRegion} this
36302 * @param {Roo.ContentPanel} panel The panel
36304 "paneladded" : true,
36306 * @event panelremoved
36307 * Fires when a panel is removed.
36308 * @param {Roo.LayoutRegion} this
36309 * @param {Roo.ContentPanel} panel The panel
36311 "panelremoved" : true,
36313 * @event beforecollapse
36314 * Fires when this region before collapse.
36315 * @param {Roo.LayoutRegion} this
36317 "beforecollapse" : true,
36320 * Fires when this region is collapsed.
36321 * @param {Roo.LayoutRegion} this
36323 "collapsed" : true,
36326 * Fires when this region is expanded.
36327 * @param {Roo.LayoutRegion} this
36332 * Fires when this region is slid into view.
36333 * @param {Roo.LayoutRegion} this
36335 "slideshow" : true,
36338 * Fires when this region slides out of view.
36339 * @param {Roo.LayoutRegion} this
36341 "slidehide" : true,
36343 * @event panelactivated
36344 * Fires when a panel is activated.
36345 * @param {Roo.LayoutRegion} this
36346 * @param {Roo.ContentPanel} panel The activated panel
36348 "panelactivated" : true,
36351 * Fires when the user resizes this region.
36352 * @param {Roo.LayoutRegion} this
36353 * @param {Number} newSize The new size (width for east/west, height for north/south)
36357 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36358 this.panels = new Roo.util.MixedCollection();
36359 this.panels.getKey = this.getPanelId.createDelegate(this);
36361 this.activePanel = null;
36362 // ensure listeners are added...
36364 if (config.listeners || config.events) {
36365 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36366 listeners : config.listeners || {},
36367 events : config.events || {}
36371 if(skipConfig !== true){
36372 this.applyConfig(config);
36376 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36378 getPanelId : function(p){
36382 applyConfig : function(config){
36383 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36384 this.config = config;
36389 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36390 * the width, for horizontal (north, south) the height.
36391 * @param {Number} newSize The new width or height
36393 resizeTo : function(newSize){
36394 var el = this.el ? this.el :
36395 (this.activePanel ? this.activePanel.getEl() : null);
36397 switch(this.position){
36400 el.setWidth(newSize);
36401 this.fireEvent("resized", this, newSize);
36405 el.setHeight(newSize);
36406 this.fireEvent("resized", this, newSize);
36412 getBox : function(){
36413 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36416 getMargins : function(){
36417 return this.margins;
36420 updateBox : function(box){
36422 var el = this.activePanel.getEl();
36423 el.dom.style.left = box.x + "px";
36424 el.dom.style.top = box.y + "px";
36425 this.activePanel.setSize(box.width, box.height);
36429 * Returns the container element for this region.
36430 * @return {Roo.Element}
36432 getEl : function(){
36433 return this.activePanel;
36437 * Returns true if this region is currently visible.
36438 * @return {Boolean}
36440 isVisible : function(){
36441 return this.activePanel ? true : false;
36444 setActivePanel : function(panel){
36445 panel = this.getPanel(panel);
36446 if(this.activePanel && this.activePanel != panel){
36447 this.activePanel.setActiveState(false);
36448 this.activePanel.getEl().setLeftTop(-10000,-10000);
36450 this.activePanel = panel;
36451 panel.setActiveState(true);
36453 panel.setSize(this.box.width, this.box.height);
36455 this.fireEvent("panelactivated", this, panel);
36456 this.fireEvent("invalidated");
36460 * Show the specified panel.
36461 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36462 * @return {Roo.ContentPanel} The shown panel or null
36464 showPanel : function(panel){
36465 panel = this.getPanel(panel);
36467 this.setActivePanel(panel);
36473 * Get the active panel for this region.
36474 * @return {Roo.ContentPanel} The active panel or null
36476 getActivePanel : function(){
36477 return this.activePanel;
36481 * Add the passed ContentPanel(s)
36482 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36483 * @return {Roo.ContentPanel} The panel added (if only one was added)
36485 add : function(panel){
36486 if(arguments.length > 1){
36487 for(var i = 0, len = arguments.length; i < len; i++) {
36488 this.add(arguments[i]);
36492 if(this.hasPanel(panel)){
36493 this.showPanel(panel);
36496 var el = panel.getEl();
36497 if(el.dom.parentNode != this.mgr.el.dom){
36498 this.mgr.el.dom.appendChild(el.dom);
36500 if(panel.setRegion){
36501 panel.setRegion(this);
36503 this.panels.add(panel);
36504 el.setStyle("position", "absolute");
36505 if(!panel.background){
36506 this.setActivePanel(panel);
36507 if(this.config.initialSize && this.panels.getCount()==1){
36508 this.resizeTo(this.config.initialSize);
36511 this.fireEvent("paneladded", this, panel);
36516 * Returns true if the panel is in this region.
36517 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36518 * @return {Boolean}
36520 hasPanel : function(panel){
36521 if(typeof panel == "object"){ // must be panel obj
36522 panel = panel.getId();
36524 return this.getPanel(panel) ? true : false;
36528 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36529 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36530 * @param {Boolean} preservePanel Overrides the config preservePanel option
36531 * @return {Roo.ContentPanel} The panel that was removed
36533 remove : function(panel, preservePanel){
36534 panel = this.getPanel(panel);
36539 this.fireEvent("beforeremove", this, panel, e);
36540 if(e.cancel === true){
36543 var panelId = panel.getId();
36544 this.panels.removeKey(panelId);
36549 * Returns the panel specified or null if it's not in this region.
36550 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36551 * @return {Roo.ContentPanel}
36553 getPanel : function(id){
36554 if(typeof id == "object"){ // must be panel obj
36557 return this.panels.get(id);
36561 * Returns this regions position (north/south/east/west/center).
36564 getPosition: function(){
36565 return this.position;
36569 * Ext JS Library 1.1.1
36570 * Copyright(c) 2006-2007, Ext JS, LLC.
36572 * Originally Released Under LGPL - original licence link has changed is not relivant.
36575 * <script type="text/javascript">
36579 * @class Roo.bootstrap.layout.Region
36580 * @extends Roo.bootstrap.layout.Basic
36581 * This class represents a region in a layout manager.
36583 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36584 * @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})
36585 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36586 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36587 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36588 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36589 * @cfg {String} title The title for the region (overrides panel titles)
36590 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36591 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36592 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36593 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36594 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36595 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36596 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36597 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36598 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36599 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36601 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36602 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36603 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36604 * @cfg {Number} width For East/West panels
36605 * @cfg {Number} height For North/South panels
36606 * @cfg {Boolean} split To show the splitter
36607 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36609 * @cfg {string} cls Extra CSS classes to add to region
36611 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36612 * @cfg {string} region the region that it inhabits..
36615 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36616 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36618 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36619 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36620 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36622 Roo.bootstrap.layout.Region = function(config)
36624 this.applyConfig(config);
36626 var mgr = config.mgr;
36627 var pos = config.region;
36628 config.skipConfig = true;
36629 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36632 this.onRender(mgr.el);
36635 this.visible = true;
36636 this.collapsed = false;
36637 this.unrendered_panels = [];
36640 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36642 position: '', // set by wrapper (eg. north/south etc..)
36643 unrendered_panels : null, // unrendered panels.
36645 tabPosition : false,
36647 mgr: false, // points to 'Border'
36650 createBody : function(){
36651 /** This region's body element
36652 * @type Roo.Element */
36653 this.bodyEl = this.el.createChild({
36655 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36659 onRender: function(ctr, pos)
36661 var dh = Roo.DomHelper;
36662 /** This region's container element
36663 * @type Roo.Element */
36664 this.el = dh.append(ctr.dom, {
36666 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36668 /** This region's title element
36669 * @type Roo.Element */
36671 this.titleEl = dh.append(this.el.dom, {
36673 unselectable: "on",
36674 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36676 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36677 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36681 this.titleEl.enableDisplayMode();
36682 /** This region's title text element
36683 * @type HTMLElement */
36684 this.titleTextEl = this.titleEl.dom.firstChild;
36685 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36687 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36688 this.closeBtn.enableDisplayMode();
36689 this.closeBtn.on("click", this.closeClicked, this);
36690 this.closeBtn.hide();
36692 this.createBody(this.config);
36693 if(this.config.hideWhenEmpty){
36695 this.on("paneladded", this.validateVisibility, this);
36696 this.on("panelremoved", this.validateVisibility, this);
36698 if(this.autoScroll){
36699 this.bodyEl.setStyle("overflow", "auto");
36701 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36703 //if(c.titlebar !== false){
36704 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36705 this.titleEl.hide();
36707 this.titleEl.show();
36708 if(this.config.title){
36709 this.titleTextEl.innerHTML = this.config.title;
36713 if(this.config.collapsed){
36714 this.collapse(true);
36716 if(this.config.hidden){
36720 if (this.unrendered_panels && this.unrendered_panels.length) {
36721 for (var i =0;i< this.unrendered_panels.length; i++) {
36722 this.add(this.unrendered_panels[i]);
36724 this.unrendered_panels = null;
36730 applyConfig : function(c)
36733 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36734 var dh = Roo.DomHelper;
36735 if(c.titlebar !== false){
36736 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36737 this.collapseBtn.on("click", this.collapse, this);
36738 this.collapseBtn.enableDisplayMode();
36740 if(c.showPin === true || this.showPin){
36741 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36742 this.stickBtn.enableDisplayMode();
36743 this.stickBtn.on("click", this.expand, this);
36744 this.stickBtn.hide();
36749 /** This region's collapsed element
36750 * @type Roo.Element */
36753 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36754 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36757 if(c.floatable !== false){
36758 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36759 this.collapsedEl.on("click", this.collapseClick, this);
36762 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36763 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36764 id: "message", unselectable: "on", style:{"float":"left"}});
36765 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36767 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36768 this.expandBtn.on("click", this.expand, this);
36772 if(this.collapseBtn){
36773 this.collapseBtn.setVisible(c.collapsible == true);
36776 this.cmargins = c.cmargins || this.cmargins ||
36777 (this.position == "west" || this.position == "east" ?
36778 {top: 0, left: 2, right:2, bottom: 0} :
36779 {top: 2, left: 0, right:0, bottom: 2});
36781 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36784 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36786 this.autoScroll = c.autoScroll || false;
36791 this.duration = c.duration || .30;
36792 this.slideDuration = c.slideDuration || .45;
36797 * Returns true if this region is currently visible.
36798 * @return {Boolean}
36800 isVisible : function(){
36801 return this.visible;
36805 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36806 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36808 //setCollapsedTitle : function(title){
36809 // title = title || " ";
36810 // if(this.collapsedTitleTextEl){
36811 // this.collapsedTitleTextEl.innerHTML = title;
36815 getBox : function(){
36817 // if(!this.collapsed){
36818 b = this.el.getBox(false, true);
36820 // b = this.collapsedEl.getBox(false, true);
36825 getMargins : function(){
36826 return this.margins;
36827 //return this.collapsed ? this.cmargins : this.margins;
36830 highlight : function(){
36831 this.el.addClass("x-layout-panel-dragover");
36834 unhighlight : function(){
36835 this.el.removeClass("x-layout-panel-dragover");
36838 updateBox : function(box)
36840 if (!this.bodyEl) {
36841 return; // not rendered yet..
36845 if(!this.collapsed){
36846 this.el.dom.style.left = box.x + "px";
36847 this.el.dom.style.top = box.y + "px";
36848 this.updateBody(box.width, box.height);
36850 this.collapsedEl.dom.style.left = box.x + "px";
36851 this.collapsedEl.dom.style.top = box.y + "px";
36852 this.collapsedEl.setSize(box.width, box.height);
36855 this.tabs.autoSizeTabs();
36859 updateBody : function(w, h)
36862 this.el.setWidth(w);
36863 w -= this.el.getBorderWidth("rl");
36864 if(this.config.adjustments){
36865 w += this.config.adjustments[0];
36868 if(h !== null && h > 0){
36869 this.el.setHeight(h);
36870 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36871 h -= this.el.getBorderWidth("tb");
36872 if(this.config.adjustments){
36873 h += this.config.adjustments[1];
36875 this.bodyEl.setHeight(h);
36877 h = this.tabs.syncHeight(h);
36880 if(this.panelSize){
36881 w = w !== null ? w : this.panelSize.width;
36882 h = h !== null ? h : this.panelSize.height;
36884 if(this.activePanel){
36885 var el = this.activePanel.getEl();
36886 w = w !== null ? w : el.getWidth();
36887 h = h !== null ? h : el.getHeight();
36888 this.panelSize = {width: w, height: h};
36889 this.activePanel.setSize(w, h);
36891 if(Roo.isIE && this.tabs){
36892 this.tabs.el.repaint();
36897 * Returns the container element for this region.
36898 * @return {Roo.Element}
36900 getEl : function(){
36905 * Hides this region.
36908 //if(!this.collapsed){
36909 this.el.dom.style.left = "-2000px";
36912 // this.collapsedEl.dom.style.left = "-2000px";
36913 // this.collapsedEl.hide();
36915 this.visible = false;
36916 this.fireEvent("visibilitychange", this, false);
36920 * Shows this region if it was previously hidden.
36923 //if(!this.collapsed){
36926 // this.collapsedEl.show();
36928 this.visible = true;
36929 this.fireEvent("visibilitychange", this, true);
36932 closeClicked : function(){
36933 if(this.activePanel){
36934 this.remove(this.activePanel);
36938 collapseClick : function(e){
36940 e.stopPropagation();
36943 e.stopPropagation();
36949 * Collapses this region.
36950 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36953 collapse : function(skipAnim, skipCheck = false){
36954 if(this.collapsed) {
36958 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36960 this.collapsed = true;
36962 this.split.el.hide();
36964 if(this.config.animate && skipAnim !== true){
36965 this.fireEvent("invalidated", this);
36966 this.animateCollapse();
36968 this.el.setLocation(-20000,-20000);
36970 this.collapsedEl.show();
36971 this.fireEvent("collapsed", this);
36972 this.fireEvent("invalidated", this);
36978 animateCollapse : function(){
36983 * Expands this region if it was previously collapsed.
36984 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36985 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36988 expand : function(e, skipAnim){
36990 e.stopPropagation();
36992 if(!this.collapsed || this.el.hasActiveFx()) {
36996 this.afterSlideIn();
36999 this.collapsed = false;
37000 if(this.config.animate && skipAnim !== true){
37001 this.animateExpand();
37005 this.split.el.show();
37007 this.collapsedEl.setLocation(-2000,-2000);
37008 this.collapsedEl.hide();
37009 this.fireEvent("invalidated", this);
37010 this.fireEvent("expanded", this);
37014 animateExpand : function(){
37018 initTabs : function()
37020 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37022 var ts = new Roo.bootstrap.panel.Tabs({
37023 el: this.bodyEl.dom,
37025 tabPosition: this.tabPosition ? this.tabPosition : 'top',
37026 disableTooltips: this.config.disableTabTips,
37027 toolbar : this.config.toolbar
37030 if(this.config.hideTabs){
37031 ts.stripWrap.setDisplayed(false);
37034 ts.resizeTabs = this.config.resizeTabs === true;
37035 ts.minTabWidth = this.config.minTabWidth || 40;
37036 ts.maxTabWidth = this.config.maxTabWidth || 250;
37037 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37038 ts.monitorResize = false;
37039 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37040 ts.bodyEl.addClass('roo-layout-tabs-body');
37041 this.panels.each(this.initPanelAsTab, this);
37044 initPanelAsTab : function(panel){
37045 var ti = this.tabs.addTab(
37049 this.config.closeOnTab && panel.isClosable(),
37052 if(panel.tabTip !== undefined){
37053 ti.setTooltip(panel.tabTip);
37055 ti.on("activate", function(){
37056 this.setActivePanel(panel);
37059 if(this.config.closeOnTab){
37060 ti.on("beforeclose", function(t, e){
37062 this.remove(panel);
37066 panel.tabItem = ti;
37071 updatePanelTitle : function(panel, title)
37073 if(this.activePanel == panel){
37074 this.updateTitle(title);
37077 var ti = this.tabs.getTab(panel.getEl().id);
37079 if(panel.tabTip !== undefined){
37080 ti.setTooltip(panel.tabTip);
37085 updateTitle : function(title){
37086 if(this.titleTextEl && !this.config.title){
37087 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
37091 setActivePanel : function(panel)
37093 panel = this.getPanel(panel);
37094 if(this.activePanel && this.activePanel != panel){
37095 if(this.activePanel.setActiveState(false) === false){
37099 this.activePanel = panel;
37100 panel.setActiveState(true);
37101 if(this.panelSize){
37102 panel.setSize(this.panelSize.width, this.panelSize.height);
37105 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37107 this.updateTitle(panel.getTitle());
37109 this.fireEvent("invalidated", this);
37111 this.fireEvent("panelactivated", this, panel);
37115 * Shows the specified panel.
37116 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37117 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37119 showPanel : function(panel)
37121 panel = this.getPanel(panel);
37124 var tab = this.tabs.getTab(panel.getEl().id);
37125 if(tab.isHidden()){
37126 this.tabs.unhideTab(tab.id);
37130 this.setActivePanel(panel);
37137 * Get the active panel for this region.
37138 * @return {Roo.ContentPanel} The active panel or null
37140 getActivePanel : function(){
37141 return this.activePanel;
37144 validateVisibility : function(){
37145 if(this.panels.getCount() < 1){
37146 this.updateTitle(" ");
37147 this.closeBtn.hide();
37150 if(!this.isVisible()){
37157 * Adds the passed ContentPanel(s) to this region.
37158 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37159 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37161 add : function(panel)
37163 if(arguments.length > 1){
37164 for(var i = 0, len = arguments.length; i < len; i++) {
37165 this.add(arguments[i]);
37170 // if we have not been rendered yet, then we can not really do much of this..
37171 if (!this.bodyEl) {
37172 this.unrendered_panels.push(panel);
37179 if(this.hasPanel(panel)){
37180 this.showPanel(panel);
37183 panel.setRegion(this);
37184 this.panels.add(panel);
37185 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37186 // sinle panel - no tab...?? would it not be better to render it with the tabs,
37187 // and hide them... ???
37188 this.bodyEl.dom.appendChild(panel.getEl().dom);
37189 if(panel.background !== true){
37190 this.setActivePanel(panel);
37192 this.fireEvent("paneladded", this, panel);
37199 this.initPanelAsTab(panel);
37203 if(panel.background !== true){
37204 this.tabs.activate(panel.getEl().id);
37206 this.fireEvent("paneladded", this, panel);
37211 * Hides the tab for the specified panel.
37212 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37214 hidePanel : function(panel){
37215 if(this.tabs && (panel = this.getPanel(panel))){
37216 this.tabs.hideTab(panel.getEl().id);
37221 * Unhides the tab for a previously hidden panel.
37222 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37224 unhidePanel : function(panel){
37225 if(this.tabs && (panel = this.getPanel(panel))){
37226 this.tabs.unhideTab(panel.getEl().id);
37230 clearPanels : function(){
37231 while(this.panels.getCount() > 0){
37232 this.remove(this.panels.first());
37237 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37238 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37239 * @param {Boolean} preservePanel Overrides the config preservePanel option
37240 * @return {Roo.ContentPanel} The panel that was removed
37242 remove : function(panel, preservePanel)
37244 panel = this.getPanel(panel);
37249 this.fireEvent("beforeremove", this, panel, e);
37250 if(e.cancel === true){
37253 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37254 var panelId = panel.getId();
37255 this.panels.removeKey(panelId);
37257 document.body.appendChild(panel.getEl().dom);
37260 this.tabs.removeTab(panel.getEl().id);
37261 }else if (!preservePanel){
37262 this.bodyEl.dom.removeChild(panel.getEl().dom);
37264 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37265 var p = this.panels.first();
37266 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37267 tempEl.appendChild(p.getEl().dom);
37268 this.bodyEl.update("");
37269 this.bodyEl.dom.appendChild(p.getEl().dom);
37271 this.updateTitle(p.getTitle());
37273 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37274 this.setActivePanel(p);
37276 panel.setRegion(null);
37277 if(this.activePanel == panel){
37278 this.activePanel = null;
37280 if(this.config.autoDestroy !== false && preservePanel !== true){
37281 try{panel.destroy();}catch(e){}
37283 this.fireEvent("panelremoved", this, panel);
37288 * Returns the TabPanel component used by this region
37289 * @return {Roo.TabPanel}
37291 getTabs : function(){
37295 createTool : function(parentEl, className){
37296 var btn = Roo.DomHelper.append(parentEl, {
37298 cls: "x-layout-tools-button",
37301 cls: "roo-layout-tools-button-inner " + className,
37305 btn.addClassOnOver("roo-layout-tools-button-over");
37310 * Ext JS Library 1.1.1
37311 * Copyright(c) 2006-2007, Ext JS, LLC.
37313 * Originally Released Under LGPL - original licence link has changed is not relivant.
37316 * <script type="text/javascript">
37322 * @class Roo.SplitLayoutRegion
37323 * @extends Roo.LayoutRegion
37324 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37326 Roo.bootstrap.layout.Split = function(config){
37327 this.cursor = config.cursor;
37328 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37331 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37333 splitTip : "Drag to resize.",
37334 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37335 useSplitTips : false,
37337 applyConfig : function(config){
37338 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37341 onRender : function(ctr,pos) {
37343 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37344 if(!this.config.split){
37349 var splitEl = Roo.DomHelper.append(ctr.dom, {
37351 id: this.el.id + "-split",
37352 cls: "roo-layout-split roo-layout-split-"+this.position,
37355 /** The SplitBar for this region
37356 * @type Roo.SplitBar */
37357 // does not exist yet...
37358 Roo.log([this.position, this.orientation]);
37360 this.split = new Roo.bootstrap.SplitBar({
37361 dragElement : splitEl,
37362 resizingElement: this.el,
37363 orientation : this.orientation
37366 this.split.on("moved", this.onSplitMove, this);
37367 this.split.useShim = this.config.useShim === true;
37368 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37369 if(this.useSplitTips){
37370 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37372 //if(config.collapsible){
37373 // this.split.el.on("dblclick", this.collapse, this);
37376 if(typeof this.config.minSize != "undefined"){
37377 this.split.minSize = this.config.minSize;
37379 if(typeof this.config.maxSize != "undefined"){
37380 this.split.maxSize = this.config.maxSize;
37382 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37383 this.hideSplitter();
37388 getHMaxSize : function(){
37389 var cmax = this.config.maxSize || 10000;
37390 var center = this.mgr.getRegion("center");
37391 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37394 getVMaxSize : function(){
37395 var cmax = this.config.maxSize || 10000;
37396 var center = this.mgr.getRegion("center");
37397 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37400 onSplitMove : function(split, newSize){
37401 this.fireEvent("resized", this, newSize);
37405 * Returns the {@link Roo.SplitBar} for this region.
37406 * @return {Roo.SplitBar}
37408 getSplitBar : function(){
37413 this.hideSplitter();
37414 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37417 hideSplitter : function(){
37419 this.split.el.setLocation(-2000,-2000);
37420 this.split.el.hide();
37426 this.split.el.show();
37428 Roo.bootstrap.layout.Split.superclass.show.call(this);
37431 beforeSlide: function(){
37432 if(Roo.isGecko){// firefox overflow auto bug workaround
37433 this.bodyEl.clip();
37435 this.tabs.bodyEl.clip();
37437 if(this.activePanel){
37438 this.activePanel.getEl().clip();
37440 if(this.activePanel.beforeSlide){
37441 this.activePanel.beforeSlide();
37447 afterSlide : function(){
37448 if(Roo.isGecko){// firefox overflow auto bug workaround
37449 this.bodyEl.unclip();
37451 this.tabs.bodyEl.unclip();
37453 if(this.activePanel){
37454 this.activePanel.getEl().unclip();
37455 if(this.activePanel.afterSlide){
37456 this.activePanel.afterSlide();
37462 initAutoHide : function(){
37463 if(this.autoHide !== false){
37464 if(!this.autoHideHd){
37465 var st = new Roo.util.DelayedTask(this.slideIn, this);
37466 this.autoHideHd = {
37467 "mouseout": function(e){
37468 if(!e.within(this.el, true)){
37472 "mouseover" : function(e){
37478 this.el.on(this.autoHideHd);
37482 clearAutoHide : function(){
37483 if(this.autoHide !== false){
37484 this.el.un("mouseout", this.autoHideHd.mouseout);
37485 this.el.un("mouseover", this.autoHideHd.mouseover);
37489 clearMonitor : function(){
37490 Roo.get(document).un("click", this.slideInIf, this);
37493 // these names are backwards but not changed for compat
37494 slideOut : function(){
37495 if(this.isSlid || this.el.hasActiveFx()){
37498 this.isSlid = true;
37499 if(this.collapseBtn){
37500 this.collapseBtn.hide();
37502 this.closeBtnState = this.closeBtn.getStyle('display');
37503 this.closeBtn.hide();
37505 this.stickBtn.show();
37508 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37509 this.beforeSlide();
37510 this.el.setStyle("z-index", 10001);
37511 this.el.slideIn(this.getSlideAnchor(), {
37512 callback: function(){
37514 this.initAutoHide();
37515 Roo.get(document).on("click", this.slideInIf, this);
37516 this.fireEvent("slideshow", this);
37523 afterSlideIn : function(){
37524 this.clearAutoHide();
37525 this.isSlid = false;
37526 this.clearMonitor();
37527 this.el.setStyle("z-index", "");
37528 if(this.collapseBtn){
37529 this.collapseBtn.show();
37531 this.closeBtn.setStyle('display', this.closeBtnState);
37533 this.stickBtn.hide();
37535 this.fireEvent("slidehide", this);
37538 slideIn : function(cb){
37539 if(!this.isSlid || this.el.hasActiveFx()){
37543 this.isSlid = false;
37544 this.beforeSlide();
37545 this.el.slideOut(this.getSlideAnchor(), {
37546 callback: function(){
37547 this.el.setLeftTop(-10000, -10000);
37549 this.afterSlideIn();
37557 slideInIf : function(e){
37558 if(!e.within(this.el)){
37563 animateCollapse : function(){
37564 this.beforeSlide();
37565 this.el.setStyle("z-index", 20000);
37566 var anchor = this.getSlideAnchor();
37567 this.el.slideOut(anchor, {
37568 callback : function(){
37569 this.el.setStyle("z-index", "");
37570 this.collapsedEl.slideIn(anchor, {duration:.3});
37572 this.el.setLocation(-10000,-10000);
37574 this.fireEvent("collapsed", this);
37581 animateExpand : function(){
37582 this.beforeSlide();
37583 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37584 this.el.setStyle("z-index", 20000);
37585 this.collapsedEl.hide({
37588 this.el.slideIn(this.getSlideAnchor(), {
37589 callback : function(){
37590 this.el.setStyle("z-index", "");
37593 this.split.el.show();
37595 this.fireEvent("invalidated", this);
37596 this.fireEvent("expanded", this);
37624 getAnchor : function(){
37625 return this.anchors[this.position];
37628 getCollapseAnchor : function(){
37629 return this.canchors[this.position];
37632 getSlideAnchor : function(){
37633 return this.sanchors[this.position];
37636 getAlignAdj : function(){
37637 var cm = this.cmargins;
37638 switch(this.position){
37654 getExpandAdj : function(){
37655 var c = this.collapsedEl, cm = this.cmargins;
37656 switch(this.position){
37658 return [-(cm.right+c.getWidth()+cm.left), 0];
37661 return [cm.right+c.getWidth()+cm.left, 0];
37664 return [0, -(cm.top+cm.bottom+c.getHeight())];
37667 return [0, cm.top+cm.bottom+c.getHeight()];
37673 * Ext JS Library 1.1.1
37674 * Copyright(c) 2006-2007, Ext JS, LLC.
37676 * Originally Released Under LGPL - original licence link has changed is not relivant.
37679 * <script type="text/javascript">
37682 * These classes are private internal classes
37684 Roo.bootstrap.layout.Center = function(config){
37685 config.region = "center";
37686 Roo.bootstrap.layout.Region.call(this, config);
37687 this.visible = true;
37688 this.minWidth = config.minWidth || 20;
37689 this.minHeight = config.minHeight || 20;
37692 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37694 // center panel can't be hidden
37698 // center panel can't be hidden
37701 getMinWidth: function(){
37702 return this.minWidth;
37705 getMinHeight: function(){
37706 return this.minHeight;
37720 Roo.bootstrap.layout.North = function(config)
37722 config.region = 'north';
37723 config.cursor = 'n-resize';
37725 Roo.bootstrap.layout.Split.call(this, config);
37729 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37730 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37731 this.split.el.addClass("roo-layout-split-v");
37733 var size = config.initialSize || config.height;
37734 if(typeof size != "undefined"){
37735 this.el.setHeight(size);
37738 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37740 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37744 getBox : function(){
37745 if(this.collapsed){
37746 return this.collapsedEl.getBox();
37748 var box = this.el.getBox();
37750 box.height += this.split.el.getHeight();
37755 updateBox : function(box){
37756 if(this.split && !this.collapsed){
37757 box.height -= this.split.el.getHeight();
37758 this.split.el.setLeft(box.x);
37759 this.split.el.setTop(box.y+box.height);
37760 this.split.el.setWidth(box.width);
37762 if(this.collapsed){
37763 this.updateBody(box.width, null);
37765 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37773 Roo.bootstrap.layout.South = function(config){
37774 config.region = 'south';
37775 config.cursor = 's-resize';
37776 Roo.bootstrap.layout.Split.call(this, config);
37778 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37779 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37780 this.split.el.addClass("roo-layout-split-v");
37782 var size = config.initialSize || config.height;
37783 if(typeof size != "undefined"){
37784 this.el.setHeight(size);
37788 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37789 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37790 getBox : function(){
37791 if(this.collapsed){
37792 return this.collapsedEl.getBox();
37794 var box = this.el.getBox();
37796 var sh = this.split.el.getHeight();
37803 updateBox : function(box){
37804 if(this.split && !this.collapsed){
37805 var sh = this.split.el.getHeight();
37808 this.split.el.setLeft(box.x);
37809 this.split.el.setTop(box.y-sh);
37810 this.split.el.setWidth(box.width);
37812 if(this.collapsed){
37813 this.updateBody(box.width, null);
37815 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37819 Roo.bootstrap.layout.East = function(config){
37820 config.region = "east";
37821 config.cursor = "e-resize";
37822 Roo.bootstrap.layout.Split.call(this, config);
37824 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37825 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37826 this.split.el.addClass("roo-layout-split-h");
37828 var size = config.initialSize || config.width;
37829 if(typeof size != "undefined"){
37830 this.el.setWidth(size);
37833 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37834 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37835 getBox : function(){
37836 if(this.collapsed){
37837 return this.collapsedEl.getBox();
37839 var box = this.el.getBox();
37841 var sw = this.split.el.getWidth();
37848 updateBox : function(box){
37849 if(this.split && !this.collapsed){
37850 var sw = this.split.el.getWidth();
37852 this.split.el.setLeft(box.x);
37853 this.split.el.setTop(box.y);
37854 this.split.el.setHeight(box.height);
37857 if(this.collapsed){
37858 this.updateBody(null, box.height);
37860 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37864 Roo.bootstrap.layout.West = function(config){
37865 config.region = "west";
37866 config.cursor = "w-resize";
37868 Roo.bootstrap.layout.Split.call(this, config);
37870 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37871 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37872 this.split.el.addClass("roo-layout-split-h");
37876 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37877 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37879 onRender: function(ctr, pos)
37881 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37882 var size = this.config.initialSize || this.config.width;
37883 if(typeof size != "undefined"){
37884 this.el.setWidth(size);
37888 getBox : function(){
37889 if(this.collapsed){
37890 return this.collapsedEl.getBox();
37892 var box = this.el.getBox();
37894 box.width += this.split.el.getWidth();
37899 updateBox : function(box){
37900 if(this.split && !this.collapsed){
37901 var sw = this.split.el.getWidth();
37903 this.split.el.setLeft(box.x+box.width);
37904 this.split.el.setTop(box.y);
37905 this.split.el.setHeight(box.height);
37907 if(this.collapsed){
37908 this.updateBody(null, box.height);
37910 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37912 });Roo.namespace("Roo.bootstrap.panel");/*
37914 * Ext JS Library 1.1.1
37915 * Copyright(c) 2006-2007, Ext JS, LLC.
37917 * Originally Released Under LGPL - original licence link has changed is not relivant.
37920 * <script type="text/javascript">
37923 * @class Roo.ContentPanel
37924 * @extends Roo.util.Observable
37925 * A basic ContentPanel element.
37926 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37927 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37928 * @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
37929 * @cfg {Boolean} closable True if the panel can be closed/removed
37930 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37931 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37932 * @cfg {Toolbar} toolbar A toolbar for this panel
37933 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37934 * @cfg {String} title The title for this panel
37935 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37936 * @cfg {String} url Calls {@link #setUrl} with this value
37937 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37938 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37939 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37940 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37941 * @cfg {Boolean} badges render the badges
37944 * Create a new ContentPanel.
37945 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37946 * @param {String/Object} config A string to set only the title or a config object
37947 * @param {String} content (optional) Set the HTML content for this panel
37948 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37950 Roo.bootstrap.panel.Content = function( config){
37952 this.tpl = config.tpl || false;
37954 var el = config.el;
37955 var content = config.content;
37957 if(config.autoCreate){ // xtype is available if this is called from factory
37960 this.el = Roo.get(el);
37961 if(!this.el && config && config.autoCreate){
37962 if(typeof config.autoCreate == "object"){
37963 if(!config.autoCreate.id){
37964 config.autoCreate.id = config.id||el;
37966 this.el = Roo.DomHelper.append(document.body,
37967 config.autoCreate, true);
37969 var elcfg = { tag: "div",
37970 cls: "roo-layout-inactive-content",
37974 elcfg.html = config.html;
37978 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37981 this.closable = false;
37982 this.loaded = false;
37983 this.active = false;
37986 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37988 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37990 this.wrapEl = this.el; //this.el.wrap();
37992 if (config.toolbar.items) {
37993 ti = config.toolbar.items ;
37994 delete config.toolbar.items ;
37998 this.toolbar.render(this.wrapEl, 'before');
37999 for(var i =0;i < ti.length;i++) {
38000 // Roo.log(['add child', items[i]]);
38001 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38003 this.toolbar.items = nitems;
38004 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38005 delete config.toolbar;
38009 // xtype created footer. - not sure if will work as we normally have to render first..
38010 if (this.footer && !this.footer.el && this.footer.xtype) {
38011 if (!this.wrapEl) {
38012 this.wrapEl = this.el.wrap();
38015 this.footer.container = this.wrapEl.createChild();
38017 this.footer = Roo.factory(this.footer, Roo);
38022 if(typeof config == "string"){
38023 this.title = config;
38025 Roo.apply(this, config);
38029 this.resizeEl = Roo.get(this.resizeEl, true);
38031 this.resizeEl = this.el;
38033 // handle view.xtype
38041 * Fires when this panel is activated.
38042 * @param {Roo.ContentPanel} this
38046 * @event deactivate
38047 * Fires when this panel is activated.
38048 * @param {Roo.ContentPanel} this
38050 "deactivate" : true,
38054 * Fires when this panel is resized if fitToFrame is true.
38055 * @param {Roo.ContentPanel} this
38056 * @param {Number} width The width after any component adjustments
38057 * @param {Number} height The height after any component adjustments
38063 * Fires when this tab is created
38064 * @param {Roo.ContentPanel} this
38075 if(this.autoScroll){
38076 this.resizeEl.setStyle("overflow", "auto");
38078 // fix randome scrolling
38079 //this.el.on('scroll', function() {
38080 // Roo.log('fix random scolling');
38081 // this.scrollTo('top',0);
38084 content = content || this.content;
38086 this.setContent(content);
38088 if(config && config.url){
38089 this.setUrl(this.url, this.params, this.loadOnce);
38094 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38096 if (this.view && typeof(this.view.xtype) != 'undefined') {
38097 this.view.el = this.el.appendChild(document.createElement("div"));
38098 this.view = Roo.factory(this.view);
38099 this.view.render && this.view.render(false, '');
38103 this.fireEvent('render', this);
38106 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38110 setRegion : function(region){
38111 this.region = region;
38112 this.setActiveClass(region && !this.background);
38116 setActiveClass: function(state)
38119 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38120 this.el.setStyle('position','relative');
38122 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38123 this.el.setStyle('position', 'absolute');
38128 * Returns the toolbar for this Panel if one was configured.
38129 * @return {Roo.Toolbar}
38131 getToolbar : function(){
38132 return this.toolbar;
38135 setActiveState : function(active)
38137 this.active = active;
38138 this.setActiveClass(active);
38140 if(this.fireEvent("deactivate", this) === false){
38145 this.fireEvent("activate", this);
38149 * Updates this panel's element
38150 * @param {String} content The new content
38151 * @param {Boolean} loadScripts (optional) true to look for and process scripts
38153 setContent : function(content, loadScripts){
38154 this.el.update(content, loadScripts);
38157 ignoreResize : function(w, h){
38158 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38161 this.lastSize = {width: w, height: h};
38166 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38167 * @return {Roo.UpdateManager} The UpdateManager
38169 getUpdateManager : function(){
38170 return this.el.getUpdateManager();
38173 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38174 * @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:
38177 url: "your-url.php",
38178 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38179 callback: yourFunction,
38180 scope: yourObject, //(optional scope)
38183 text: "Loading...",
38188 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38189 * 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.
38190 * @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}
38191 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38192 * @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.
38193 * @return {Roo.ContentPanel} this
38196 var um = this.el.getUpdateManager();
38197 um.update.apply(um, arguments);
38203 * 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.
38204 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38205 * @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)
38206 * @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)
38207 * @return {Roo.UpdateManager} The UpdateManager
38209 setUrl : function(url, params, loadOnce){
38210 if(this.refreshDelegate){
38211 this.removeListener("activate", this.refreshDelegate);
38213 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38214 this.on("activate", this.refreshDelegate);
38215 return this.el.getUpdateManager();
38218 _handleRefresh : function(url, params, loadOnce){
38219 if(!loadOnce || !this.loaded){
38220 var updater = this.el.getUpdateManager();
38221 updater.update(url, params, this._setLoaded.createDelegate(this));
38225 _setLoaded : function(){
38226 this.loaded = true;
38230 * Returns this panel's id
38233 getId : function(){
38238 * Returns this panel's element - used by regiosn to add.
38239 * @return {Roo.Element}
38241 getEl : function(){
38242 return this.wrapEl || this.el;
38247 adjustForComponents : function(width, height)
38249 //Roo.log('adjustForComponents ');
38250 if(this.resizeEl != this.el){
38251 width -= this.el.getFrameWidth('lr');
38252 height -= this.el.getFrameWidth('tb');
38255 var te = this.toolbar.getEl();
38256 te.setWidth(width);
38257 height -= te.getHeight();
38260 var te = this.footer.getEl();
38261 te.setWidth(width);
38262 height -= te.getHeight();
38266 if(this.adjustments){
38267 width += this.adjustments[0];
38268 height += this.adjustments[1];
38270 return {"width": width, "height": height};
38273 setSize : function(width, height){
38274 if(this.fitToFrame && !this.ignoreResize(width, height)){
38275 if(this.fitContainer && this.resizeEl != this.el){
38276 this.el.setSize(width, height);
38278 var size = this.adjustForComponents(width, height);
38279 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38280 this.fireEvent('resize', this, size.width, size.height);
38285 * Returns this panel's title
38288 getTitle : function(){
38290 if (typeof(this.title) != 'object') {
38295 for (var k in this.title) {
38296 if (!this.title.hasOwnProperty(k)) {
38300 if (k.indexOf('-') >= 0) {
38301 var s = k.split('-');
38302 for (var i = 0; i<s.length; i++) {
38303 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38306 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38313 * Set this panel's title
38314 * @param {String} title
38316 setTitle : function(title){
38317 this.title = title;
38319 this.region.updatePanelTitle(this, title);
38324 * Returns true is this panel was configured to be closable
38325 * @return {Boolean}
38327 isClosable : function(){
38328 return this.closable;
38331 beforeSlide : function(){
38333 this.resizeEl.clip();
38336 afterSlide : function(){
38338 this.resizeEl.unclip();
38342 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38343 * Will fail silently if the {@link #setUrl} method has not been called.
38344 * This does not activate the panel, just updates its content.
38346 refresh : function(){
38347 if(this.refreshDelegate){
38348 this.loaded = false;
38349 this.refreshDelegate();
38354 * Destroys this panel
38356 destroy : function(){
38357 this.el.removeAllListeners();
38358 var tempEl = document.createElement("span");
38359 tempEl.appendChild(this.el.dom);
38360 tempEl.innerHTML = "";
38366 * form - if the content panel contains a form - this is a reference to it.
38367 * @type {Roo.form.Form}
38371 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38372 * This contains a reference to it.
38378 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38388 * @param {Object} cfg Xtype definition of item to add.
38392 getChildContainer: function () {
38393 return this.getEl();
38398 var ret = new Roo.factory(cfg);
38403 if (cfg.xtype.match(/^Form$/)) {
38406 //if (this.footer) {
38407 // el = this.footer.container.insertSibling(false, 'before');
38409 el = this.el.createChild();
38412 this.form = new Roo.form.Form(cfg);
38415 if ( this.form.allItems.length) {
38416 this.form.render(el.dom);
38420 // should only have one of theses..
38421 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38422 // views.. should not be just added - used named prop 'view''
38424 cfg.el = this.el.appendChild(document.createElement("div"));
38427 var ret = new Roo.factory(cfg);
38429 ret.render && ret.render(false, ''); // render blank..
38439 * @class Roo.bootstrap.panel.Grid
38440 * @extends Roo.bootstrap.panel.Content
38442 * Create a new GridPanel.
38443 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38444 * @param {Object} config A the config object
38450 Roo.bootstrap.panel.Grid = function(config)
38454 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38455 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38457 config.el = this.wrapper;
38458 //this.el = this.wrapper;
38460 if (config.container) {
38461 // ctor'ed from a Border/panel.grid
38464 this.wrapper.setStyle("overflow", "hidden");
38465 this.wrapper.addClass('roo-grid-container');
38470 if(config.toolbar){
38471 var tool_el = this.wrapper.createChild();
38472 this.toolbar = Roo.factory(config.toolbar);
38474 if (config.toolbar.items) {
38475 ti = config.toolbar.items ;
38476 delete config.toolbar.items ;
38480 this.toolbar.render(tool_el);
38481 for(var i =0;i < ti.length;i++) {
38482 // Roo.log(['add child', items[i]]);
38483 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38485 this.toolbar.items = nitems;
38487 delete config.toolbar;
38490 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38491 config.grid.scrollBody = true;;
38492 config.grid.monitorWindowResize = false; // turn off autosizing
38493 config.grid.autoHeight = false;
38494 config.grid.autoWidth = false;
38496 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38498 if (config.background) {
38499 // render grid on panel activation (if panel background)
38500 this.on('activate', function(gp) {
38501 if (!gp.grid.rendered) {
38502 gp.grid.render(this.wrapper);
38503 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38508 this.grid.render(this.wrapper);
38509 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38512 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38513 // ??? needed ??? config.el = this.wrapper;
38518 // xtype created footer. - not sure if will work as we normally have to render first..
38519 if (this.footer && !this.footer.el && this.footer.xtype) {
38521 var ctr = this.grid.getView().getFooterPanel(true);
38522 this.footer.dataSource = this.grid.dataSource;
38523 this.footer = Roo.factory(this.footer, Roo);
38524 this.footer.render(ctr);
38534 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38535 getId : function(){
38536 return this.grid.id;
38540 * Returns the grid for this panel
38541 * @return {Roo.bootstrap.Table}
38543 getGrid : function(){
38547 setSize : function(width, height){
38548 if(!this.ignoreResize(width, height)){
38549 var grid = this.grid;
38550 var size = this.adjustForComponents(width, height);
38551 var gridel = grid.getGridEl();
38552 gridel.setSize(size.width, size.height);
38554 var thd = grid.getGridEl().select('thead',true).first();
38555 var tbd = grid.getGridEl().select('tbody', true).first();
38557 tbd.setSize(width, height - thd.getHeight());
38566 beforeSlide : function(){
38567 this.grid.getView().scroller.clip();
38570 afterSlide : function(){
38571 this.grid.getView().scroller.unclip();
38574 destroy : function(){
38575 this.grid.destroy();
38577 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38582 * @class Roo.bootstrap.panel.Nest
38583 * @extends Roo.bootstrap.panel.Content
38585 * Create a new Panel, that can contain a layout.Border.
38588 * @param {Roo.BorderLayout} layout The layout for this panel
38589 * @param {String/Object} config A string to set only the title or a config object
38591 Roo.bootstrap.panel.Nest = function(config)
38593 // construct with only one argument..
38594 /* FIXME - implement nicer consturctors
38595 if (layout.layout) {
38597 layout = config.layout;
38598 delete config.layout;
38600 if (layout.xtype && !layout.getEl) {
38601 // then layout needs constructing..
38602 layout = Roo.factory(layout, Roo);
38606 config.el = config.layout.getEl();
38608 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38610 config.layout.monitorWindowResize = false; // turn off autosizing
38611 this.layout = config.layout;
38612 this.layout.getEl().addClass("roo-layout-nested-layout");
38613 this.layout.parent = this;
38620 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38622 setSize : function(width, height){
38623 if(!this.ignoreResize(width, height)){
38624 var size = this.adjustForComponents(width, height);
38625 var el = this.layout.getEl();
38626 if (size.height < 1) {
38627 el.setWidth(size.width);
38629 el.setSize(size.width, size.height);
38631 var touch = el.dom.offsetWidth;
38632 this.layout.layout();
38633 // ie requires a double layout on the first pass
38634 if(Roo.isIE && !this.initialized){
38635 this.initialized = true;
38636 this.layout.layout();
38641 // activate all subpanels if not currently active..
38643 setActiveState : function(active){
38644 this.active = active;
38645 this.setActiveClass(active);
38648 this.fireEvent("deactivate", this);
38652 this.fireEvent("activate", this);
38653 // not sure if this should happen before or after..
38654 if (!this.layout) {
38655 return; // should not happen..
38658 for (var r in this.layout.regions) {
38659 reg = this.layout.getRegion(r);
38660 if (reg.getActivePanel()) {
38661 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38662 reg.setActivePanel(reg.getActivePanel());
38665 if (!reg.panels.length) {
38668 reg.showPanel(reg.getPanel(0));
38677 * Returns the nested BorderLayout for this panel
38678 * @return {Roo.BorderLayout}
38680 getLayout : function(){
38681 return this.layout;
38685 * Adds a xtype elements to the layout of the nested panel
38689 xtype : 'ContentPanel',
38696 xtype : 'NestedLayoutPanel',
38702 items : [ ... list of content panels or nested layout panels.. ]
38706 * @param {Object} cfg Xtype definition of item to add.
38708 addxtype : function(cfg) {
38709 return this.layout.addxtype(cfg);
38714 * Ext JS Library 1.1.1
38715 * Copyright(c) 2006-2007, Ext JS, LLC.
38717 * Originally Released Under LGPL - original licence link has changed is not relivant.
38720 * <script type="text/javascript">
38723 * @class Roo.TabPanel
38724 * @extends Roo.util.Observable
38725 * A lightweight tab container.
38729 // basic tabs 1, built from existing content
38730 var tabs = new Roo.TabPanel("tabs1");
38731 tabs.addTab("script", "View Script");
38732 tabs.addTab("markup", "View Markup");
38733 tabs.activate("script");
38735 // more advanced tabs, built from javascript
38736 var jtabs = new Roo.TabPanel("jtabs");
38737 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38739 // set up the UpdateManager
38740 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38741 var updater = tab2.getUpdateManager();
38742 updater.setDefaultUrl("ajax1.htm");
38743 tab2.on('activate', updater.refresh, updater, true);
38745 // Use setUrl for Ajax loading
38746 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38747 tab3.setUrl("ajax2.htm", null, true);
38750 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38753 jtabs.activate("jtabs-1");
38756 * Create a new TabPanel.
38757 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38758 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38760 Roo.bootstrap.panel.Tabs = function(config){
38762 * The container element for this TabPanel.
38763 * @type Roo.Element
38765 this.el = Roo.get(config.el);
38768 if(typeof config == "boolean"){
38769 this.tabPosition = config ? "bottom" : "top";
38771 Roo.apply(this, config);
38775 if(this.tabPosition == "bottom"){
38776 // if tabs are at the bottom = create the body first.
38777 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38778 this.el.addClass("roo-tabs-bottom");
38780 // next create the tabs holders
38782 if (this.tabPosition == "west"){
38784 var reg = this.region; // fake it..
38786 if (!reg.mgr.parent) {
38789 reg = reg.mgr.parent.region;
38791 Roo.log("got nest?");
38793 if (reg.mgr.getRegion('west')) {
38794 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38795 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38796 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38797 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38798 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38806 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38807 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38808 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38809 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38814 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38817 // finally - if tabs are at the top, then create the body last..
38818 if(this.tabPosition != "bottom"){
38819 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38820 * @type Roo.Element
38822 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38823 this.el.addClass("roo-tabs-top");
38827 this.bodyEl.setStyle("position", "relative");
38829 this.active = null;
38830 this.activateDelegate = this.activate.createDelegate(this);
38835 * Fires when the active tab changes
38836 * @param {Roo.TabPanel} this
38837 * @param {Roo.TabPanelItem} activePanel The new active tab
38841 * @event beforetabchange
38842 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38843 * @param {Roo.TabPanel} this
38844 * @param {Object} e Set cancel to true on this object to cancel the tab change
38845 * @param {Roo.TabPanelItem} tab The tab being changed to
38847 "beforetabchange" : true
38850 Roo.EventManager.onWindowResize(this.onResize, this);
38851 this.cpad = this.el.getPadding("lr");
38852 this.hiddenCount = 0;
38855 // toolbar on the tabbar support...
38856 if (this.toolbar) {
38857 alert("no toolbar support yet");
38858 this.toolbar = false;
38860 var tcfg = this.toolbar;
38861 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38862 this.toolbar = new Roo.Toolbar(tcfg);
38863 if (Roo.isSafari) {
38864 var tbl = tcfg.container.child('table', true);
38865 tbl.setAttribute('width', '100%');
38873 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38876 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38878 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38880 tabPosition : "top",
38882 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38884 currentTabWidth : 0,
38886 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38890 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38894 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38896 preferredTabWidth : 175,
38898 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38900 resizeTabs : false,
38902 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38904 monitorResize : true,
38906 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38908 toolbar : false, // set by caller..
38910 region : false, /// set by caller
38912 disableTooltips : true, // not used yet...
38915 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38916 * @param {String} id The id of the div to use <b>or create</b>
38917 * @param {String} text The text for the tab
38918 * @param {String} content (optional) Content to put in the TabPanelItem body
38919 * @param {Boolean} closable (optional) True to create a close icon on the tab
38920 * @return {Roo.TabPanelItem} The created TabPanelItem
38922 addTab : function(id, text, content, closable, tpl)
38924 var item = new Roo.bootstrap.panel.TabItem({
38928 closable : closable,
38931 this.addTabItem(item);
38933 item.setContent(content);
38939 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38940 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38941 * @return {Roo.TabPanelItem}
38943 getTab : function(id){
38944 return this.items[id];
38948 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38949 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38951 hideTab : function(id){
38952 var t = this.items[id];
38955 this.hiddenCount++;
38956 this.autoSizeTabs();
38961 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38962 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38964 unhideTab : function(id){
38965 var t = this.items[id];
38967 t.setHidden(false);
38968 this.hiddenCount--;
38969 this.autoSizeTabs();
38974 * Adds an existing {@link Roo.TabPanelItem}.
38975 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38977 addTabItem : function(item)
38979 this.items[item.id] = item;
38980 this.items.push(item);
38981 this.autoSizeTabs();
38982 // if(this.resizeTabs){
38983 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38984 // this.autoSizeTabs();
38986 // item.autoSize();
38991 * Removes a {@link Roo.TabPanelItem}.
38992 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38994 removeTab : function(id){
38995 var items = this.items;
38996 var tab = items[id];
38997 if(!tab) { return; }
38998 var index = items.indexOf(tab);
38999 if(this.active == tab && items.length > 1){
39000 var newTab = this.getNextAvailable(index);
39005 this.stripEl.dom.removeChild(tab.pnode.dom);
39006 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39007 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39009 items.splice(index, 1);
39010 delete this.items[tab.id];
39011 tab.fireEvent("close", tab);
39012 tab.purgeListeners();
39013 this.autoSizeTabs();
39016 getNextAvailable : function(start){
39017 var items = this.items;
39019 // look for a next tab that will slide over to
39020 // replace the one being removed
39021 while(index < items.length){
39022 var item = items[++index];
39023 if(item && !item.isHidden()){
39027 // if one isn't found select the previous tab (on the left)
39030 var item = items[--index];
39031 if(item && !item.isHidden()){
39039 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39040 * @param {String/Number} id The id or index of the TabPanelItem to disable.
39042 disableTab : function(id){
39043 var tab = this.items[id];
39044 if(tab && this.active != tab){
39050 * Enables a {@link Roo.TabPanelItem} that is disabled.
39051 * @param {String/Number} id The id or index of the TabPanelItem to enable.
39053 enableTab : function(id){
39054 var tab = this.items[id];
39059 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39060 * @param {String/Number} id The id or index of the TabPanelItem to activate.
39061 * @return {Roo.TabPanelItem} The TabPanelItem.
39063 activate : function(id)
39065 //Roo.log('activite:' + id);
39067 var tab = this.items[id];
39071 if(tab == this.active || tab.disabled){
39075 this.fireEvent("beforetabchange", this, e, tab);
39076 if(e.cancel !== true && !tab.disabled){
39078 this.active.hide();
39080 this.active = this.items[id];
39081 this.active.show();
39082 this.fireEvent("tabchange", this, this.active);
39088 * Gets the active {@link Roo.TabPanelItem}.
39089 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39091 getActiveTab : function(){
39092 return this.active;
39096 * Updates the tab body element to fit the height of the container element
39097 * for overflow scrolling
39098 * @param {Number} targetHeight (optional) Override the starting height from the elements height
39100 syncHeight : function(targetHeight){
39101 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39102 var bm = this.bodyEl.getMargins();
39103 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39104 this.bodyEl.setHeight(newHeight);
39108 onResize : function(){
39109 if(this.monitorResize){
39110 this.autoSizeTabs();
39115 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39117 beginUpdate : function(){
39118 this.updating = true;
39122 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39124 endUpdate : function(){
39125 this.updating = false;
39126 this.autoSizeTabs();
39130 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39132 autoSizeTabs : function()
39134 var count = this.items.length;
39135 var vcount = count - this.hiddenCount;
39138 this.stripEl.hide();
39140 this.stripEl.show();
39143 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39148 var w = Math.max(this.el.getWidth() - this.cpad, 10);
39149 var availWidth = Math.floor(w / vcount);
39150 var b = this.stripBody;
39151 if(b.getWidth() > w){
39152 var tabs = this.items;
39153 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39154 if(availWidth < this.minTabWidth){
39155 /*if(!this.sleft){ // incomplete scrolling code
39156 this.createScrollButtons();
39159 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39162 if(this.currentTabWidth < this.preferredTabWidth){
39163 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39169 * Returns the number of tabs in this TabPanel.
39172 getCount : function(){
39173 return this.items.length;
39177 * Resizes all the tabs to the passed width
39178 * @param {Number} The new width
39180 setTabWidth : function(width){
39181 this.currentTabWidth = width;
39182 for(var i = 0, len = this.items.length; i < len; i++) {
39183 if(!this.items[i].isHidden()) {
39184 this.items[i].setWidth(width);
39190 * Destroys this TabPanel
39191 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39193 destroy : function(removeEl){
39194 Roo.EventManager.removeResizeListener(this.onResize, this);
39195 for(var i = 0, len = this.items.length; i < len; i++){
39196 this.items[i].purgeListeners();
39198 if(removeEl === true){
39199 this.el.update("");
39204 createStrip : function(container)
39206 var strip = document.createElement("nav");
39207 strip.className = Roo.bootstrap.version == 4 ?
39208 "navbar-light bg-light" :
39209 "navbar navbar-default"; //"x-tabs-wrap";
39210 container.appendChild(strip);
39214 createStripList : function(strip)
39216 // div wrapper for retard IE
39217 // returns the "tr" element.
39218 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39219 //'<div class="x-tabs-strip-wrap">'+
39220 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39221 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39222 return strip.firstChild; //.firstChild.firstChild.firstChild;
39224 createBody : function(container)
39226 var body = document.createElement("div");
39227 Roo.id(body, "tab-body");
39228 //Roo.fly(body).addClass("x-tabs-body");
39229 Roo.fly(body).addClass("tab-content");
39230 container.appendChild(body);
39233 createItemBody :function(bodyEl, id){
39234 var body = Roo.getDom(id);
39236 body = document.createElement("div");
39239 //Roo.fly(body).addClass("x-tabs-item-body");
39240 Roo.fly(body).addClass("tab-pane");
39241 bodyEl.insertBefore(body, bodyEl.firstChild);
39245 createStripElements : function(stripEl, text, closable, tpl)
39247 var td = document.createElement("li"); // was td..
39248 td.className = 'nav-item';
39250 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39253 stripEl.appendChild(td);
39255 td.className = "x-tabs-closable";
39256 if(!this.closeTpl){
39257 this.closeTpl = new Roo.Template(
39258 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39259 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39260 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39263 var el = this.closeTpl.overwrite(td, {"text": text});
39264 var close = el.getElementsByTagName("div")[0];
39265 var inner = el.getElementsByTagName("em")[0];
39266 return {"el": el, "close": close, "inner": inner};
39269 // not sure what this is..
39270 // if(!this.tabTpl){
39271 //this.tabTpl = new Roo.Template(
39272 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39273 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39275 // this.tabTpl = new Roo.Template(
39276 // '<a href="#">' +
39277 // '<span unselectable="on"' +
39278 // (this.disableTooltips ? '' : ' title="{text}"') +
39279 // ' >{text}</span></a>'
39285 var template = tpl || this.tabTpl || false;
39288 template = new Roo.Template(
39289 Roo.bootstrap.version == 4 ?
39291 '<a class="nav-link" href="#" unselectable="on"' +
39292 (this.disableTooltips ? '' : ' title="{text}"') +
39295 '<a class="nav-link" href="#">' +
39296 '<span unselectable="on"' +
39297 (this.disableTooltips ? '' : ' title="{text}"') +
39298 ' >{text}</span></a>'
39303 switch (typeof(template)) {
39307 template = new Roo.Template(template);
39313 var el = template.overwrite(td, {"text": text});
39315 var inner = el.getElementsByTagName("span")[0];
39317 return {"el": el, "inner": inner};
39325 * @class Roo.TabPanelItem
39326 * @extends Roo.util.Observable
39327 * Represents an individual item (tab plus body) in a TabPanel.
39328 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39329 * @param {String} id The id of this TabPanelItem
39330 * @param {String} text The text for the tab of this TabPanelItem
39331 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39333 Roo.bootstrap.panel.TabItem = function(config){
39335 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39336 * @type Roo.TabPanel
39338 this.tabPanel = config.panel;
39340 * The id for this TabPanelItem
39343 this.id = config.id;
39345 this.disabled = false;
39347 this.text = config.text;
39349 this.loaded = false;
39350 this.closable = config.closable;
39353 * The body element for this TabPanelItem.
39354 * @type Roo.Element
39356 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39357 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39358 this.bodyEl.setStyle("display", "block");
39359 this.bodyEl.setStyle("zoom", "1");
39360 //this.hideAction();
39362 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39364 this.el = Roo.get(els.el);
39365 this.inner = Roo.get(els.inner, true);
39366 this.textEl = Roo.bootstrap.version == 4 ?
39367 this.el : Roo.get(this.el.dom.firstChild, true);
39369 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39370 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39373 // this.el.on("mousedown", this.onTabMouseDown, this);
39374 this.el.on("click", this.onTabClick, this);
39376 if(config.closable){
39377 var c = Roo.get(els.close, true);
39378 c.dom.title = this.closeText;
39379 c.addClassOnOver("close-over");
39380 c.on("click", this.closeClick, this);
39386 * Fires when this tab becomes the active tab.
39387 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39388 * @param {Roo.TabPanelItem} this
39392 * @event beforeclose
39393 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39394 * @param {Roo.TabPanelItem} this
39395 * @param {Object} e Set cancel to true on this object to cancel the close.
39397 "beforeclose": true,
39400 * Fires when this tab is closed.
39401 * @param {Roo.TabPanelItem} this
39405 * @event deactivate
39406 * Fires when this tab is no longer the active tab.
39407 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39408 * @param {Roo.TabPanelItem} this
39410 "deactivate" : true
39412 this.hidden = false;
39414 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39417 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39419 purgeListeners : function(){
39420 Roo.util.Observable.prototype.purgeListeners.call(this);
39421 this.el.removeAllListeners();
39424 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39427 this.status_node.addClass("active");
39430 this.tabPanel.stripWrap.repaint();
39432 this.fireEvent("activate", this.tabPanel, this);
39436 * Returns true if this tab is the active tab.
39437 * @return {Boolean}
39439 isActive : function(){
39440 return this.tabPanel.getActiveTab() == this;
39444 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39447 this.status_node.removeClass("active");
39449 this.fireEvent("deactivate", this.tabPanel, this);
39452 hideAction : function(){
39453 this.bodyEl.hide();
39454 this.bodyEl.setStyle("position", "absolute");
39455 this.bodyEl.setLeft("-20000px");
39456 this.bodyEl.setTop("-20000px");
39459 showAction : function(){
39460 this.bodyEl.setStyle("position", "relative");
39461 this.bodyEl.setTop("");
39462 this.bodyEl.setLeft("");
39463 this.bodyEl.show();
39467 * Set the tooltip for the tab.
39468 * @param {String} tooltip The tab's tooltip
39470 setTooltip : function(text){
39471 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39472 this.textEl.dom.qtip = text;
39473 this.textEl.dom.removeAttribute('title');
39475 this.textEl.dom.title = text;
39479 onTabClick : function(e){
39480 e.preventDefault();
39481 this.tabPanel.activate(this.id);
39484 onTabMouseDown : function(e){
39485 e.preventDefault();
39486 this.tabPanel.activate(this.id);
39489 getWidth : function(){
39490 return this.inner.getWidth();
39493 setWidth : function(width){
39494 var iwidth = width - this.linode.getPadding("lr");
39495 this.inner.setWidth(iwidth);
39496 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39497 this.linode.setWidth(width);
39501 * Show or hide the tab
39502 * @param {Boolean} hidden True to hide or false to show.
39504 setHidden : function(hidden){
39505 this.hidden = hidden;
39506 this.linode.setStyle("display", hidden ? "none" : "");
39510 * Returns true if this tab is "hidden"
39511 * @return {Boolean}
39513 isHidden : function(){
39514 return this.hidden;
39518 * Returns the text for this tab
39521 getText : function(){
39525 autoSize : function(){
39526 //this.el.beginMeasure();
39527 this.textEl.setWidth(1);
39529 * #2804 [new] Tabs in Roojs
39530 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39532 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39533 //this.el.endMeasure();
39537 * Sets the text for the tab (Note: this also sets the tooltip text)
39538 * @param {String} text The tab's text and tooltip
39540 setText : function(text){
39542 this.textEl.update(text);
39543 this.setTooltip(text);
39544 //if(!this.tabPanel.resizeTabs){
39545 // this.autoSize();
39549 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39551 activate : function(){
39552 this.tabPanel.activate(this.id);
39556 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39558 disable : function(){
39559 if(this.tabPanel.active != this){
39560 this.disabled = true;
39561 this.status_node.addClass("disabled");
39566 * Enables this TabPanelItem if it was previously disabled.
39568 enable : function(){
39569 this.disabled = false;
39570 this.status_node.removeClass("disabled");
39574 * Sets the content for this TabPanelItem.
39575 * @param {String} content The content
39576 * @param {Boolean} loadScripts true to look for and load scripts
39578 setContent : function(content, loadScripts){
39579 this.bodyEl.update(content, loadScripts);
39583 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39584 * @return {Roo.UpdateManager} The UpdateManager
39586 getUpdateManager : function(){
39587 return this.bodyEl.getUpdateManager();
39591 * Set a URL to be used to load the content for this TabPanelItem.
39592 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39593 * @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)
39594 * @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)
39595 * @return {Roo.UpdateManager} The UpdateManager
39597 setUrl : function(url, params, loadOnce){
39598 if(this.refreshDelegate){
39599 this.un('activate', this.refreshDelegate);
39601 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39602 this.on("activate", this.refreshDelegate);
39603 return this.bodyEl.getUpdateManager();
39607 _handleRefresh : function(url, params, loadOnce){
39608 if(!loadOnce || !this.loaded){
39609 var updater = this.bodyEl.getUpdateManager();
39610 updater.update(url, params, this._setLoaded.createDelegate(this));
39615 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39616 * Will fail silently if the setUrl method has not been called.
39617 * This does not activate the panel, just updates its content.
39619 refresh : function(){
39620 if(this.refreshDelegate){
39621 this.loaded = false;
39622 this.refreshDelegate();
39627 _setLoaded : function(){
39628 this.loaded = true;
39632 closeClick : function(e){
39635 this.fireEvent("beforeclose", this, o);
39636 if(o.cancel !== true){
39637 this.tabPanel.removeTab(this.id);
39641 * The text displayed in the tooltip for the close icon.
39644 closeText : "Close this tab"
39647 * This script refer to:
39648 * Title: International Telephone Input
39649 * Author: Jack O'Connor
39650 * Code version: v12.1.12
39651 * Availability: https://github.com/jackocnr/intl-tel-input.git
39654 Roo.bootstrap.PhoneInputData = function() {
39657 "Afghanistan (افغانستان)",
39662 "Albania (Shqipëri)",
39667 "Algeria (الجزائر)",
39692 "Antigua and Barbuda",
39702 "Armenia (Հայաստան)",
39718 "Austria (Österreich)",
39723 "Azerbaijan (Azərbaycan)",
39733 "Bahrain (البحرين)",
39738 "Bangladesh (বাংলাদেশ)",
39748 "Belarus (Беларусь)",
39753 "Belgium (België)",
39783 "Bosnia and Herzegovina (Босна и Херцеговина)",
39798 "British Indian Ocean Territory",
39803 "British Virgin Islands",
39813 "Bulgaria (България)",
39823 "Burundi (Uburundi)",
39828 "Cambodia (កម្ពុជា)",
39833 "Cameroon (Cameroun)",
39842 ["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"]
39845 "Cape Verde (Kabu Verdi)",
39850 "Caribbean Netherlands",
39861 "Central African Republic (République centrafricaine)",
39881 "Christmas Island",
39887 "Cocos (Keeling) Islands",
39898 "Comoros (جزر القمر)",
39903 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39908 "Congo (Republic) (Congo-Brazzaville)",
39928 "Croatia (Hrvatska)",
39949 "Czech Republic (Česká republika)",
39954 "Denmark (Danmark)",
39969 "Dominican Republic (República Dominicana)",
39973 ["809", "829", "849"]
39991 "Equatorial Guinea (Guinea Ecuatorial)",
40011 "Falkland Islands (Islas Malvinas)",
40016 "Faroe Islands (Føroyar)",
40037 "French Guiana (Guyane française)",
40042 "French Polynesia (Polynésie française)",
40057 "Georgia (საქართველო)",
40062 "Germany (Deutschland)",
40082 "Greenland (Kalaallit Nunaat)",
40119 "Guinea-Bissau (Guiné Bissau)",
40144 "Hungary (Magyarország)",
40149 "Iceland (Ísland)",
40169 "Iraq (العراق)",
40185 "Israel (ישראל)",
40212 "Jordan (الأردن)",
40217 "Kazakhstan (Казахстан)",
40238 "Kuwait (الكويت)",
40243 "Kyrgyzstan (Кыргызстан)",
40253 "Latvia (Latvija)",
40258 "Lebanon (لبنان)",
40273 "Libya (ليبيا)",
40283 "Lithuania (Lietuva)",
40298 "Macedonia (FYROM) (Македонија)",
40303 "Madagascar (Madagasikara)",
40333 "Marshall Islands",
40343 "Mauritania (موريتانيا)",
40348 "Mauritius (Moris)",
40369 "Moldova (Republica Moldova)",
40379 "Mongolia (Монгол)",
40384 "Montenegro (Crna Gora)",
40394 "Morocco (المغرب)",
40400 "Mozambique (Moçambique)",
40405 "Myanmar (Burma) (မြန်မာ)",
40410 "Namibia (Namibië)",
40425 "Netherlands (Nederland)",
40430 "New Caledonia (Nouvelle-Calédonie)",
40465 "North Korea (조선 민주주의 인민 공화국)",
40470 "Northern Mariana Islands",
40486 "Pakistan (پاکستان)",
40496 "Palestine (فلسطين)",
40506 "Papua New Guinea",
40548 "Réunion (La Réunion)",
40554 "Romania (România)",
40570 "Saint Barthélemy",
40581 "Saint Kitts and Nevis",
40591 "Saint Martin (Saint-Martin (partie française))",
40597 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40602 "Saint Vincent and the Grenadines",
40617 "São Tomé and Príncipe (São Tomé e Príncipe)",
40622 "Saudi Arabia (المملكة العربية السعودية)",
40627 "Senegal (Sénégal)",
40657 "Slovakia (Slovensko)",
40662 "Slovenia (Slovenija)",
40672 "Somalia (Soomaaliya)",
40682 "South Korea (대한민국)",
40687 "South Sudan (جنوب السودان)",
40697 "Sri Lanka (ශ්රී ලංකාව)",
40702 "Sudan (السودان)",
40712 "Svalbard and Jan Mayen",
40723 "Sweden (Sverige)",
40728 "Switzerland (Schweiz)",
40733 "Syria (سوريا)",
40778 "Trinidad and Tobago",
40783 "Tunisia (تونس)",
40788 "Turkey (Türkiye)",
40798 "Turks and Caicos Islands",
40808 "U.S. Virgin Islands",
40818 "Ukraine (Україна)",
40823 "United Arab Emirates (الإمارات العربية المتحدة)",
40845 "Uzbekistan (Oʻzbekiston)",
40855 "Vatican City (Città del Vaticano)",
40866 "Vietnam (Việt Nam)",
40871 "Wallis and Futuna (Wallis-et-Futuna)",
40876 "Western Sahara (الصحراء الغربية)",
40882 "Yemen (اليمن)",
40906 * This script refer to:
40907 * Title: International Telephone Input
40908 * Author: Jack O'Connor
40909 * Code version: v12.1.12
40910 * Availability: https://github.com/jackocnr/intl-tel-input.git
40914 * @class Roo.bootstrap.PhoneInput
40915 * @extends Roo.bootstrap.TriggerField
40916 * An input with International dial-code selection
40918 * @cfg {String} defaultDialCode default '+852'
40919 * @cfg {Array} preferedCountries default []
40922 * Create a new PhoneInput.
40923 * @param {Object} config Configuration options
40926 Roo.bootstrap.PhoneInput = function(config) {
40927 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40930 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40932 listWidth: undefined,
40934 selectedClass: 'active',
40936 invalidClass : "has-warning",
40938 validClass: 'has-success',
40940 allowed: '0123456789',
40945 * @cfg {String} defaultDialCode The default dial code when initializing the input
40947 defaultDialCode: '+852',
40950 * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
40952 preferedCountries: false,
40954 getAutoCreate : function()
40956 var data = Roo.bootstrap.PhoneInputData();
40957 var align = this.labelAlign || this.parentLabelAlign();
40960 this.allCountries = [];
40961 this.dialCodeMapping = [];
40963 for (var i = 0; i < data.length; i++) {
40965 this.allCountries[i] = {
40969 priority: c[3] || 0,
40970 areaCodes: c[4] || null
40972 this.dialCodeMapping[c[2]] = {
40975 priority: c[3] || 0,
40976 areaCodes: c[4] || null
40988 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40989 maxlength: this.max_length,
40990 cls : 'form-control tel-input',
40991 autocomplete: 'new-password'
40994 var hiddenInput = {
40997 cls: 'hidden-tel-input'
41001 hiddenInput.name = this.name;
41004 if (this.disabled) {
41005 input.disabled = true;
41008 var flag_container = {
41025 cls: this.hasFeedback ? 'has-feedback' : '',
41031 cls: 'dial-code-holder',
41038 cls: 'roo-select2-container input-group',
41045 if (this.fieldLabel.length) {
41048 tooltip: 'This field is required'
41054 cls: 'control-label',
41060 html: this.fieldLabel
41063 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41069 if(this.indicatorpos == 'right') {
41070 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41077 if(align == 'left') {
41085 if(this.labelWidth > 12){
41086 label.style = "width: " + this.labelWidth + 'px';
41088 if(this.labelWidth < 13 && this.labelmd == 0){
41089 this.labelmd = this.labelWidth;
41091 if(this.labellg > 0){
41092 label.cls += ' col-lg-' + this.labellg;
41093 input.cls += ' col-lg-' + (12 - this.labellg);
41095 if(this.labelmd > 0){
41096 label.cls += ' col-md-' + this.labelmd;
41097 container.cls += ' col-md-' + (12 - this.labelmd);
41099 if(this.labelsm > 0){
41100 label.cls += ' col-sm-' + this.labelsm;
41101 container.cls += ' col-sm-' + (12 - this.labelsm);
41103 if(this.labelxs > 0){
41104 label.cls += ' col-xs-' + this.labelxs;
41105 container.cls += ' col-xs-' + (12 - this.labelxs);
41115 var settings = this;
41117 ['xs','sm','md','lg'].map(function(size){
41118 if (settings[size]) {
41119 cfg.cls += ' col-' + size + '-' + settings[size];
41123 this.store = new Roo.data.Store({
41124 proxy : new Roo.data.MemoryProxy({}),
41125 reader : new Roo.data.JsonReader({
41136 'name' : 'dialCode',
41140 'name' : 'priority',
41144 'name' : 'areaCodes',
41151 if(!this.preferedCountries) {
41152 this.preferedCountries = [
41159 var p = this.preferedCountries.reverse();
41162 for (var i = 0; i < p.length; i++) {
41163 for (var j = 0; j < this.allCountries.length; j++) {
41164 if(this.allCountries[j].iso2 == p[i]) {
41165 var t = this.allCountries[j];
41166 this.allCountries.splice(j,1);
41167 this.allCountries.unshift(t);
41173 this.store.proxy.data = {
41175 data: this.allCountries
41181 initEvents : function()
41184 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41186 this.indicator = this.indicatorEl();
41187 this.flag = this.flagEl();
41188 this.dialCodeHolder = this.dialCodeHolderEl();
41190 this.trigger = this.el.select('div.flag-box',true).first();
41191 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41196 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41197 _this.list.setWidth(lw);
41200 this.list.on('mouseover', this.onViewOver, this);
41201 this.list.on('mousemove', this.onViewMove, this);
41202 this.inputEl().on("keyup", this.onKeyUp, this);
41203 this.inputEl().on("keypress", this.onKeyPress, this);
41205 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41207 this.view = new Roo.View(this.list, this.tpl, {
41208 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41211 this.view.on('click', this.onViewClick, this);
41212 this.setValue(this.defaultDialCode);
41215 onTriggerClick : function(e)
41217 Roo.log('trigger click');
41222 if(this.isExpanded()){
41224 this.hasFocus = false;
41226 this.store.load({});
41227 this.hasFocus = true;
41232 isExpanded : function()
41234 return this.list.isVisible();
41237 collapse : function()
41239 if(!this.isExpanded()){
41243 Roo.get(document).un('mousedown', this.collapseIf, this);
41244 Roo.get(document).un('mousewheel', this.collapseIf, this);
41245 this.fireEvent('collapse', this);
41249 expand : function()
41253 if(this.isExpanded() || !this.hasFocus){
41257 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41258 this.list.setWidth(lw);
41261 this.restrictHeight();
41263 Roo.get(document).on('mousedown', this.collapseIf, this);
41264 Roo.get(document).on('mousewheel', this.collapseIf, this);
41266 this.fireEvent('expand', this);
41269 restrictHeight : function()
41271 this.list.alignTo(this.inputEl(), this.listAlign);
41272 this.list.alignTo(this.inputEl(), this.listAlign);
41275 onViewOver : function(e, t)
41277 if(this.inKeyMode){
41280 var item = this.view.findItemFromChild(t);
41283 var index = this.view.indexOf(item);
41284 this.select(index, false);
41289 onViewClick : function(view, doFocus, el, e)
41291 var index = this.view.getSelectedIndexes()[0];
41293 var r = this.store.getAt(index);
41296 this.onSelect(r, index);
41298 if(doFocus !== false && !this.blockFocus){
41299 this.inputEl().focus();
41303 onViewMove : function(e, t)
41305 this.inKeyMode = false;
41308 select : function(index, scrollIntoView)
41310 this.selectedIndex = index;
41311 this.view.select(index);
41312 if(scrollIntoView !== false){
41313 var el = this.view.getNode(index);
41315 this.list.scrollChildIntoView(el, false);
41320 createList : function()
41322 this.list = Roo.get(document.body).createChild({
41324 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41325 style: 'display:none'
41328 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41331 collapseIf : function(e)
41333 var in_combo = e.within(this.el);
41334 var in_list = e.within(this.list);
41335 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41337 if (in_combo || in_list || is_list) {
41343 onSelect : function(record, index)
41345 if(this.fireEvent('beforeselect', this, record, index) !== false){
41347 this.setFlagClass(record.data.iso2);
41348 this.setDialCode(record.data.dialCode);
41349 this.hasFocus = false;
41351 this.fireEvent('select', this, record, index);
41355 flagEl : function()
41357 var flag = this.el.select('div.flag',true).first();
41364 dialCodeHolderEl : function()
41366 var d = this.el.select('input.dial-code-holder',true).first();
41373 setDialCode : function(v)
41375 this.dialCodeHolder.dom.value = '+'+v;
41378 setFlagClass : function(n)
41380 this.flag.dom.className = 'flag '+n;
41383 getValue : function()
41385 var v = this.inputEl().getValue();
41386 if(this.dialCodeHolder) {
41387 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41392 setValue : function(v)
41394 var d = this.getDialCode(v);
41396 //invalid dial code
41397 if(v.length == 0 || !d || d.length == 0) {
41399 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41400 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41406 this.setFlagClass(this.dialCodeMapping[d].iso2);
41407 this.setDialCode(d);
41408 this.inputEl().dom.value = v.replace('+'+d,'');
41409 this.hiddenEl().dom.value = this.getValue();
41414 getDialCode : function(v)
41418 if (v.length == 0) {
41419 return this.dialCodeHolder.dom.value;
41423 if (v.charAt(0) != "+") {
41426 var numericChars = "";
41427 for (var i = 1; i < v.length; i++) {
41428 var c = v.charAt(i);
41431 if (this.dialCodeMapping[numericChars]) {
41432 dialCode = v.substr(1, i);
41434 if (numericChars.length == 4) {
41444 this.setValue(this.defaultDialCode);
41448 hiddenEl : function()
41450 return this.el.select('input.hidden-tel-input',true).first();
41453 // after setting val
41454 onKeyUp : function(e){
41455 this.setValue(this.getValue());
41458 onKeyPress : function(e){
41459 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41466 * @class Roo.bootstrap.MoneyField
41467 * @extends Roo.bootstrap.ComboBox
41468 * Bootstrap MoneyField class
41471 * Create a new MoneyField.
41472 * @param {Object} config Configuration options
41475 Roo.bootstrap.MoneyField = function(config) {
41477 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41481 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41484 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41486 allowDecimals : true,
41488 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41490 decimalSeparator : ".",
41492 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41494 decimalPrecision : 0,
41496 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41498 allowNegative : true,
41500 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41504 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41506 minValue : Number.NEGATIVE_INFINITY,
41508 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41510 maxValue : Number.MAX_VALUE,
41512 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41514 minText : "The minimum value for this field is {0}",
41516 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41518 maxText : "The maximum value for this field is {0}",
41520 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41521 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41523 nanText : "{0} is not a valid number",
41525 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41529 * @cfg {String} defaults currency of the MoneyField
41530 * value should be in lkey
41532 defaultCurrency : false,
41534 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41536 thousandsDelimiter : false,
41538 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41549 getAutoCreate : function()
41551 var align = this.labelAlign || this.parentLabelAlign();
41563 cls : 'form-control roo-money-amount-input',
41564 autocomplete: 'new-password'
41567 var hiddenInput = {
41571 cls: 'hidden-number-input'
41574 if(this.max_length) {
41575 input.maxlength = this.max_length;
41579 hiddenInput.name = this.name;
41582 if (this.disabled) {
41583 input.disabled = true;
41586 var clg = 12 - this.inputlg;
41587 var cmd = 12 - this.inputmd;
41588 var csm = 12 - this.inputsm;
41589 var cxs = 12 - this.inputxs;
41593 cls : 'row roo-money-field',
41597 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41601 cls: 'roo-select2-container input-group',
41605 cls : 'form-control roo-money-currency-input',
41606 autocomplete: 'new-password',
41608 name : this.currencyName
41612 cls : 'input-group-addon',
41626 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41630 cls: this.hasFeedback ? 'has-feedback' : '',
41641 if (this.fieldLabel.length) {
41644 tooltip: 'This field is required'
41650 cls: 'control-label',
41656 html: this.fieldLabel
41659 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41665 if(this.indicatorpos == 'right') {
41666 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41673 if(align == 'left') {
41681 if(this.labelWidth > 12){
41682 label.style = "width: " + this.labelWidth + 'px';
41684 if(this.labelWidth < 13 && this.labelmd == 0){
41685 this.labelmd = this.labelWidth;
41687 if(this.labellg > 0){
41688 label.cls += ' col-lg-' + this.labellg;
41689 input.cls += ' col-lg-' + (12 - this.labellg);
41691 if(this.labelmd > 0){
41692 label.cls += ' col-md-' + this.labelmd;
41693 container.cls += ' col-md-' + (12 - this.labelmd);
41695 if(this.labelsm > 0){
41696 label.cls += ' col-sm-' + this.labelsm;
41697 container.cls += ' col-sm-' + (12 - this.labelsm);
41699 if(this.labelxs > 0){
41700 label.cls += ' col-xs-' + this.labelxs;
41701 container.cls += ' col-xs-' + (12 - this.labelxs);
41712 var settings = this;
41714 ['xs','sm','md','lg'].map(function(size){
41715 if (settings[size]) {
41716 cfg.cls += ' col-' + size + '-' + settings[size];
41723 initEvents : function()
41725 this.indicator = this.indicatorEl();
41727 this.initCurrencyEvent();
41729 this.initNumberEvent();
41732 initCurrencyEvent : function()
41735 throw "can not find store for combo";
41738 this.store = Roo.factory(this.store, Roo.data);
41739 this.store.parent = this;
41743 this.triggerEl = this.el.select('.input-group-addon', true).first();
41745 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41750 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41751 _this.list.setWidth(lw);
41754 this.list.on('mouseover', this.onViewOver, this);
41755 this.list.on('mousemove', this.onViewMove, this);
41756 this.list.on('scroll', this.onViewScroll, this);
41759 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41762 this.view = new Roo.View(this.list, this.tpl, {
41763 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41766 this.view.on('click', this.onViewClick, this);
41768 this.store.on('beforeload', this.onBeforeLoad, this);
41769 this.store.on('load', this.onLoad, this);
41770 this.store.on('loadexception', this.onLoadException, this);
41772 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41773 "up" : function(e){
41774 this.inKeyMode = true;
41778 "down" : function(e){
41779 if(!this.isExpanded()){
41780 this.onTriggerClick();
41782 this.inKeyMode = true;
41787 "enter" : function(e){
41790 if(this.fireEvent("specialkey", this, e)){
41791 this.onViewClick(false);
41797 "esc" : function(e){
41801 "tab" : function(e){
41804 if(this.fireEvent("specialkey", this, e)){
41805 this.onViewClick(false);
41813 doRelay : function(foo, bar, hname){
41814 if(hname == 'down' || this.scope.isExpanded()){
41815 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41823 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41827 initNumberEvent : function(e)
41829 this.inputEl().on("keydown" , this.fireKey, this);
41830 this.inputEl().on("focus", this.onFocus, this);
41831 this.inputEl().on("blur", this.onBlur, this);
41833 this.inputEl().relayEvent('keyup', this);
41835 if(this.indicator){
41836 this.indicator.addClass('invisible');
41839 this.originalValue = this.getValue();
41841 if(this.validationEvent == 'keyup'){
41842 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41843 this.inputEl().on('keyup', this.filterValidation, this);
41845 else if(this.validationEvent !== false){
41846 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41849 if(this.selectOnFocus){
41850 this.on("focus", this.preFocus, this);
41853 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41854 this.inputEl().on("keypress", this.filterKeys, this);
41856 this.inputEl().relayEvent('keypress', this);
41859 var allowed = "0123456789";
41861 if(this.allowDecimals){
41862 allowed += this.decimalSeparator;
41865 if(this.allowNegative){
41869 if(this.thousandsDelimiter) {
41873 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41875 var keyPress = function(e){
41877 var k = e.getKey();
41879 var c = e.getCharCode();
41882 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41883 allowed.indexOf(String.fromCharCode(c)) === -1
41889 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41893 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41898 this.inputEl().on("keypress", keyPress, this);
41902 onTriggerClick : function(e)
41909 this.loadNext = false;
41911 if(this.isExpanded()){
41916 this.hasFocus = true;
41918 if(this.triggerAction == 'all') {
41919 this.doQuery(this.allQuery, true);
41923 this.doQuery(this.getRawValue());
41926 getCurrency : function()
41928 var v = this.currencyEl().getValue();
41933 restrictHeight : function()
41935 this.list.alignTo(this.currencyEl(), this.listAlign);
41936 this.list.alignTo(this.currencyEl(), this.listAlign);
41939 onViewClick : function(view, doFocus, el, e)
41941 var index = this.view.getSelectedIndexes()[0];
41943 var r = this.store.getAt(index);
41946 this.onSelect(r, index);
41950 onSelect : function(record, index){
41952 if(this.fireEvent('beforeselect', this, record, index) !== false){
41954 this.setFromCurrencyData(index > -1 ? record.data : false);
41958 this.fireEvent('select', this, record, index);
41962 setFromCurrencyData : function(o)
41966 this.lastCurrency = o;
41968 if (this.currencyField) {
41969 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41971 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41974 this.lastSelectionText = currency;
41976 //setting default currency
41977 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41978 this.setCurrency(this.defaultCurrency);
41982 this.setCurrency(currency);
41985 setFromData : function(o)
41989 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41991 this.setFromCurrencyData(c);
41996 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41998 Roo.log('no value set for '+ (this.name ? this.name : this.id));
42001 this.setValue(value);
42005 setCurrency : function(v)
42007 this.currencyValue = v;
42010 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42015 setValue : function(v)
42017 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42023 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42025 this.inputEl().dom.value = (v == '') ? '' :
42026 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42028 if(!this.allowZero && v === '0') {
42029 this.hiddenEl().dom.value = '';
42030 this.inputEl().dom.value = '';
42037 getRawValue : function()
42039 var v = this.inputEl().getValue();
42044 getValue : function()
42046 return this.fixPrecision(this.parseValue(this.getRawValue()));
42049 parseValue : function(value)
42051 if(this.thousandsDelimiter) {
42053 r = new RegExp(",", "g");
42054 value = value.replace(r, "");
42057 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42058 return isNaN(value) ? '' : value;
42062 fixPrecision : function(value)
42064 if(this.thousandsDelimiter) {
42066 r = new RegExp(",", "g");
42067 value = value.replace(r, "");
42070 var nan = isNaN(value);
42072 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42073 return nan ? '' : value;
42075 return parseFloat(value).toFixed(this.decimalPrecision);
42078 decimalPrecisionFcn : function(v)
42080 return Math.floor(v);
42083 validateValue : function(value)
42085 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42089 var num = this.parseValue(value);
42092 this.markInvalid(String.format(this.nanText, value));
42096 if(num < this.minValue){
42097 this.markInvalid(String.format(this.minText, this.minValue));
42101 if(num > this.maxValue){
42102 this.markInvalid(String.format(this.maxText, this.maxValue));
42109 validate : function()
42111 if(this.disabled || this.allowBlank){
42116 var currency = this.getCurrency();
42118 if(this.validateValue(this.getRawValue()) && currency.length){
42123 this.markInvalid();
42127 getName: function()
42132 beforeBlur : function()
42138 var v = this.parseValue(this.getRawValue());
42145 onBlur : function()
42149 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42150 //this.el.removeClass(this.focusClass);
42153 this.hasFocus = false;
42155 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42159 var v = this.getValue();
42161 if(String(v) !== String(this.startValue)){
42162 this.fireEvent('change', this, v, this.startValue);
42165 this.fireEvent("blur", this);
42168 inputEl : function()
42170 return this.el.select('.roo-money-amount-input', true).first();
42173 currencyEl : function()
42175 return this.el.select('.roo-money-currency-input', true).first();
42178 hiddenEl : function()
42180 return this.el.select('input.hidden-number-input',true).first();
42184 * @class Roo.bootstrap.BezierSignature
42185 * @extends Roo.bootstrap.Component
42186 * Bootstrap BezierSignature class
42187 * This script refer to:
42188 * Title: Signature Pad
42190 * Availability: https://github.com/szimek/signature_pad
42193 * Create a new BezierSignature
42194 * @param {Object} config The config object
42197 Roo.bootstrap.BezierSignature = function(config){
42198 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42204 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42211 mouse_btn_down: true,
42214 * @cfg {int} canvas height
42216 canvas_height: '200px',
42219 * @cfg {float|function} Radius of a single dot.
42224 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42229 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42234 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42239 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42244 * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
42246 bg_color: 'rgba(0, 0, 0, 0)',
42249 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42251 dot_color: 'black',
42254 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42256 velocity_filter_weight: 0.7,
42259 * @cfg {function} Callback when stroke begin.
42264 * @cfg {function} Callback when stroke end.
42268 getAutoCreate : function()
42270 var cls = 'roo-signature column';
42273 cls += ' ' + this.cls;
42283 for(var i = 0; i < col_sizes.length; i++) {
42284 if(this[col_sizes[i]]) {
42285 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42295 cls: 'roo-signature-body',
42299 cls: 'roo-signature-body-canvas',
42300 height: this.canvas_height,
42301 width: this.canvas_width
42308 style: 'display: none'
42316 initEvents: function()
42318 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42320 var canvas = this.canvasEl();
42322 // mouse && touch event swapping...
42323 canvas.dom.style.touchAction = 'none';
42324 canvas.dom.style.msTouchAction = 'none';
42326 this.mouse_btn_down = false;
42327 canvas.on('mousedown', this._handleMouseDown, this);
42328 canvas.on('mousemove', this._handleMouseMove, this);
42329 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42331 if (window.PointerEvent) {
42332 canvas.on('pointerdown', this._handleMouseDown, this);
42333 canvas.on('pointermove', this._handleMouseMove, this);
42334 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42337 if ('ontouchstart' in window) {
42338 canvas.on('touchstart', this._handleTouchStart, this);
42339 canvas.on('touchmove', this._handleTouchMove, this);
42340 canvas.on('touchend', this._handleTouchEnd, this);
42343 Roo.EventManager.onWindowResize(this.resize, this, true);
42345 // file input event
42346 this.fileEl().on('change', this.uploadImage, this);
42353 resize: function(){
42355 var canvas = this.canvasEl().dom;
42356 var ctx = this.canvasElCtx();
42357 var img_data = false;
42359 if(canvas.width > 0) {
42360 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42362 // setting canvas width will clean img data
42365 var style = window.getComputedStyle ?
42366 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42368 var padding_left = parseInt(style.paddingLeft) || 0;
42369 var padding_right = parseInt(style.paddingRight) || 0;
42371 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42374 ctx.putImageData(img_data, 0, 0);
42378 _handleMouseDown: function(e)
42380 if (e.browserEvent.which === 1) {
42381 this.mouse_btn_down = true;
42382 this.strokeBegin(e);
42386 _handleMouseMove: function (e)
42388 if (this.mouse_btn_down) {
42389 this.strokeMoveUpdate(e);
42393 _handleMouseUp: function (e)
42395 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42396 this.mouse_btn_down = false;
42401 _handleTouchStart: function (e) {
42403 e.preventDefault();
42404 if (e.browserEvent.targetTouches.length === 1) {
42405 // var touch = e.browserEvent.changedTouches[0];
42406 // this.strokeBegin(touch);
42408 this.strokeBegin(e); // assume e catching the correct xy...
42412 _handleTouchMove: function (e) {
42413 e.preventDefault();
42414 // var touch = event.targetTouches[0];
42415 // _this._strokeMoveUpdate(touch);
42416 this.strokeMoveUpdate(e);
42419 _handleTouchEnd: function (e) {
42420 var wasCanvasTouched = e.target === this.canvasEl().dom;
42421 if (wasCanvasTouched) {
42422 e.preventDefault();
42423 // var touch = event.changedTouches[0];
42424 // _this._strokeEnd(touch);
42429 reset: function () {
42430 this._lastPoints = [];
42431 this._lastVelocity = 0;
42432 this._lastWidth = (this.min_width + this.max_width) / 2;
42433 this.canvasElCtx().fillStyle = this.dot_color;
42436 strokeMoveUpdate: function(e)
42438 this.strokeUpdate(e);
42440 if (this.throttle) {
42441 this.throttleStroke(this.strokeUpdate, this.throttle);
42444 this.strokeUpdate(e);
42448 strokeBegin: function(e)
42450 var newPointGroup = {
42451 color: this.dot_color,
42455 if (typeof this.onBegin === 'function') {
42459 this.curve_data.push(newPointGroup);
42461 this.strokeUpdate(e);
42464 strokeUpdate: function(e)
42466 var rect = this.canvasEl().dom.getBoundingClientRect();
42467 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42468 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42469 var lastPoints = lastPointGroup.points;
42470 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42471 var isLastPointTooClose = lastPoint
42472 ? point.distanceTo(lastPoint) <= this.min_distance
42474 var color = lastPointGroup.color;
42475 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42476 var curve = this.addPoint(point);
42478 this.drawDot({color: color, point: point});
42481 this.drawCurve({color: color, curve: curve});
42491 strokeEnd: function(e)
42493 this.strokeUpdate(e);
42494 if (typeof this.onEnd === 'function') {
42499 addPoint: function (point) {
42500 var _lastPoints = this._lastPoints;
42501 _lastPoints.push(point);
42502 if (_lastPoints.length > 2) {
42503 if (_lastPoints.length === 3) {
42504 _lastPoints.unshift(_lastPoints[0]);
42506 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42507 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42508 _lastPoints.shift();
42514 calculateCurveWidths: function (startPoint, endPoint) {
42515 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42516 (1 - this.velocity_filter_weight) * this._lastVelocity;
42518 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42521 start: this._lastWidth
42524 this._lastVelocity = velocity;
42525 this._lastWidth = newWidth;
42529 drawDot: function (_a) {
42530 var color = _a.color, point = _a.point;
42531 var ctx = this.canvasElCtx();
42532 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42534 this.drawCurveSegment(point.x, point.y, width);
42536 ctx.fillStyle = color;
42540 drawCurve: function (_a) {
42541 var color = _a.color, curve = _a.curve;
42542 var ctx = this.canvasElCtx();
42543 var widthDelta = curve.endWidth - curve.startWidth;
42544 var drawSteps = Math.floor(curve.length()) * 2;
42546 ctx.fillStyle = color;
42547 for (var i = 0; i < drawSteps; i += 1) {
42548 var t = i / drawSteps;
42554 var x = uuu * curve.startPoint.x;
42555 x += 3 * uu * t * curve.control1.x;
42556 x += 3 * u * tt * curve.control2.x;
42557 x += ttt * curve.endPoint.x;
42558 var y = uuu * curve.startPoint.y;
42559 y += 3 * uu * t * curve.control1.y;
42560 y += 3 * u * tt * curve.control2.y;
42561 y += ttt * curve.endPoint.y;
42562 var width = curve.startWidth + ttt * widthDelta;
42563 this.drawCurveSegment(x, y, width);
42569 drawCurveSegment: function (x, y, width) {
42570 var ctx = this.canvasElCtx();
42572 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42573 this.is_empty = false;
42578 var ctx = this.canvasElCtx();
42579 var canvas = this.canvasEl().dom;
42580 ctx.fillStyle = this.bg_color;
42581 ctx.clearRect(0, 0, canvas.width, canvas.height);
42582 ctx.fillRect(0, 0, canvas.width, canvas.height);
42583 this.curve_data = [];
42585 this.is_empty = true;
42590 return this.el.select('input',true).first();
42593 canvasEl: function()
42595 return this.el.select('canvas',true).first();
42598 canvasElCtx: function()
42600 return this.el.select('canvas',true).first().dom.getContext('2d');
42603 getImage: function(type)
42605 if(this.is_empty) {
42610 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42613 drawFromImage: function(img_src)
42615 var img = new Image();
42617 img.onload = function(){
42618 this.canvasElCtx().drawImage(img, 0, 0);
42623 this.is_empty = false;
42626 selectImage: function()
42628 this.fileEl().dom.click();
42631 uploadImage: function(e)
42633 var reader = new FileReader();
42635 reader.onload = function(e){
42636 var img = new Image();
42637 img.onload = function(){
42639 this.canvasElCtx().drawImage(img, 0, 0);
42641 img.src = e.target.result;
42644 reader.readAsDataURL(e.target.files[0]);
42647 // Bezier Point Constructor
42648 Point: (function () {
42649 function Point(x, y, time) {
42652 this.time = time || Date.now();
42654 Point.prototype.distanceTo = function (start) {
42655 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42657 Point.prototype.equals = function (other) {
42658 return this.x === other.x && this.y === other.y && this.time === other.time;
42660 Point.prototype.velocityFrom = function (start) {
42661 return this.time !== start.time
42662 ? this.distanceTo(start) / (this.time - start.time)
42669 // Bezier Constructor
42670 Bezier: (function () {
42671 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42672 this.startPoint = startPoint;
42673 this.control2 = control2;
42674 this.control1 = control1;
42675 this.endPoint = endPoint;
42676 this.startWidth = startWidth;
42677 this.endWidth = endWidth;
42679 Bezier.fromPoints = function (points, widths, scope) {
42680 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42681 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42682 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42684 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42685 var dx1 = s1.x - s2.x;
42686 var dy1 = s1.y - s2.y;
42687 var dx2 = s2.x - s3.x;
42688 var dy2 = s2.y - s3.y;
42689 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42690 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42691 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42692 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42693 var dxm = m1.x - m2.x;
42694 var dym = m1.y - m2.y;
42695 var k = l2 / (l1 + l2);
42696 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42697 var tx = s2.x - cm.x;
42698 var ty = s2.y - cm.y;
42700 c1: new scope.Point(m1.x + tx, m1.y + ty),
42701 c2: new scope.Point(m2.x + tx, m2.y + ty)
42704 Bezier.prototype.length = function () {
42709 for (var i = 0; i <= steps; i += 1) {
42711 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42712 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42714 var xdiff = cx - px;
42715 var ydiff = cy - py;
42716 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42723 Bezier.prototype.point = function (t, start, c1, c2, end) {
42724 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42725 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42726 + (3.0 * c2 * (1.0 - t) * t * t)
42727 + (end * t * t * t);
42732 throttleStroke: function(fn, wait) {
42733 if (wait === void 0) { wait = 250; }
42735 var timeout = null;
42739 var later = function () {
42740 previous = Date.now();
42742 result = fn.apply(storedContext, storedArgs);
42744 storedContext = null;
42748 return function wrapper() {
42750 for (var _i = 0; _i < arguments.length; _i++) {
42751 args[_i] = arguments[_i];
42753 var now = Date.now();
42754 var remaining = wait - (now - previous);
42755 storedContext = this;
42757 if (remaining <= 0 || remaining > wait) {
42759 clearTimeout(timeout);
42763 result = fn.apply(storedContext, storedArgs);
42765 storedContext = null;
42769 else if (!timeout) {
42770 timeout = window.setTimeout(later, remaining);