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/)) {
17 * Ext JS Library 1.1.1
18 * Copyright(c) 2006-2007, Ext JS, LLC.
20 * Originally Released Under LGPL - original licence link has changed is not relivant.
23 * <script type="text/javascript">
29 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
30 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
31 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34 * @param {Object} config The config object
36 Roo.Shadow = function(config){
37 Roo.apply(this, config);
38 if(typeof this.mode != "string"){
39 this.mode = this.defaultMode;
41 var o = this.offset, a = {h: 0};
42 var rad = Math.floor(this.offset/2);
43 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49 a.l -= this.offset + rad;
50 a.t -= this.offset + rad;
61 a.l -= (this.offset - rad);
62 a.t -= this.offset + rad;
64 a.w -= (this.offset - rad)*2;
75 a.l -= (this.offset - rad);
76 a.t -= (this.offset - rad);
78 a.w -= (this.offset + rad + 1);
79 a.h -= (this.offset + rad);
88 Roo.Shadow.prototype = {
91 * The shadow display mode. Supports the following options:<br />
92 * sides: Shadow displays on both sides and bottom only<br />
93 * frame: Shadow displays equally on all four sides<br />
94 * drop: Traditional bottom-right drop shadow (default)
97 * @cfg {String} offset
98 * The number of pixels to offset the shadow from the element (defaults to 4)
106 * Displays the shadow under the target element
107 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
109 show : function(target){
110 target = Roo.get(target);
112 this.el = Roo.Shadow.Pool.pull();
113 if(this.el.dom.nextSibling != target.dom){
114 this.el.insertBefore(target);
117 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
119 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122 target.getLeft(true),
127 this.el.dom.style.display = "block";
131 * Returns true if the shadow is visible, else false
133 isVisible : function(){
134 return this.el ? true : false;
138 * Direct alignment when values are already available. Show must be called at least once before
139 * calling this method to ensure it is initialized.
140 * @param {Number} left The target element left position
141 * @param {Number} top The target element top position
142 * @param {Number} width The target element width
143 * @param {Number} height The target element height
145 realign : function(l, t, w, h){
149 var a = this.adjusts, d = this.el.dom, s = d.style;
151 s.left = (l+a.l)+"px";
152 s.top = (t+a.t)+"px";
153 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
155 if(s.width != sws || s.height != shs){
159 var cn = d.childNodes;
160 var sww = Math.max(0, (sw-12))+"px";
161 cn[0].childNodes[1].style.width = sww;
162 cn[1].childNodes[1].style.width = sww;
163 cn[2].childNodes[1].style.width = sww;
164 cn[1].style.height = Math.max(0, (sh-12))+"px";
174 this.el.dom.style.display = "none";
175 Roo.Shadow.Pool.push(this.el);
181 * Adjust the z-index of this shadow
182 * @param {Number} zindex The new z-index
184 setZIndex : function(z){
187 this.el.setStyle("z-index", z);
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
195 var markup = Roo.isIE ?
196 '<div class="x-ie-shadow"></div>' :
197 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
202 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203 sh.autoBoxAdjust = false;
215 * base class for bootstrap elements.
219 Roo.bootstrap = Roo.bootstrap || {};
221 * @class Roo.bootstrap.Component
222 * @extends Roo.Component
223 * Bootstrap Component base class
224 * @cfg {String} cls css class
225 * @cfg {String} style any extra css
226 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
228 * @cfg {string} dataId cutomer id
229 * @cfg {string} name Specifies name attribute
230 * @cfg {string} tooltip Text for the tooltip
231 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
232 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235 * Do not use directly - it does not do anything..
236 * @param {Object} config The config object
241 Roo.bootstrap.Component = function(config){
242 Roo.bootstrap.Component.superclass.constructor.call(this, config);
246 * @event childrenrendered
247 * Fires when the children have been rendered..
248 * @param {Roo.bootstrap.Component} this
250 "childrenrendered" : true
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
262 allowDomMove : false, // to stop relocations in parent onRender...
272 * Initialize Events for the element
274 initEvents : function() { },
280 can_build_overlaid : true,
282 container_method : false,
289 // returns the parent component..
290 return Roo.ComponentMgr.get(this.parentId)
296 onRender : function(ct, position)
298 // Roo.log("Call onRender: " + this.xtype);
300 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303 if (this.el.attr('xtype')) {
304 this.el.attr('xtypex', this.el.attr('xtype'));
305 this.el.dom.removeAttribute('xtype');
315 var cfg = Roo.apply({}, this.getAutoCreate());
317 cfg.id = this.id || Roo.id();
319 // fill in the extra attributes
320 if (this.xattr && typeof(this.xattr) =='object') {
321 for (var i in this.xattr) {
322 cfg[i] = this.xattr[i];
327 cfg.dataId = this.dataId;
331 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334 if (this.style) { // fixme needs to support more complex style data.
335 cfg.style = this.style;
339 cfg.name = this.name;
342 this.el = ct.createChild(cfg, position);
345 this.tooltipEl().attr('tooltip', this.tooltip);
348 if(this.tabIndex !== undefined){
349 this.el.dom.setAttribute('tabIndex', this.tabIndex);
356 * Fetch the element to add children to
357 * @return {Roo.Element} defaults to this.el
359 getChildContainer : function()
364 * Fetch the element to display the tooltip on.
365 * @return {Roo.Element} defaults to this.el
367 tooltipEl : function()
372 addxtype : function(tree,cntr)
376 cn = Roo.factory(tree);
377 //Roo.log(['addxtype', cn]);
379 cn.parentType = this.xtype; //??
380 cn.parentId = this.id;
382 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383 if (typeof(cn.container_method) == 'string') {
384 cntr = cn.container_method;
388 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
390 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
392 var build_from_html = Roo.XComponent.build_from_html;
394 var is_body = (tree.xtype == 'Body') ;
396 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
398 var self_cntr_el = Roo.get(this[cntr](false));
400 // do not try and build conditional elements
401 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
405 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407 return this.addxtypeChild(tree,cntr, is_body);
410 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416 Roo.log('skipping render');
422 if (!build_from_html) {
426 // this i think handles overlaying multiple children of the same type
427 // with the sam eelement.. - which might be buggy..
429 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
435 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
439 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
446 addxtypeChild : function (tree, cntr, is_body)
448 Roo.debug && Roo.log('addxtypeChild:' + cntr);
450 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454 (typeof(tree['flexy:foreach']) != 'undefined');
458 skip_children = false;
459 // render the element if it's not BODY.
462 // if parent was disabled, then do not try and create the children..
463 if(!this[cntr](true)){
468 cn = Roo.factory(tree);
470 cn.parentType = this.xtype; //??
471 cn.parentId = this.id;
473 var build_from_html = Roo.XComponent.build_from_html;
476 // does the container contain child eleemnts with 'xtype' attributes.
477 // that match this xtype..
478 // note - when we render we create these as well..
479 // so we should check to see if body has xtype set.
480 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
482 var self_cntr_el = Roo.get(this[cntr](false));
483 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
485 //Roo.log(Roo.XComponent.build_from_html);
486 //Roo.log("got echild:");
489 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490 // and are not displayed -this causes this to use up the wrong element when matching.
491 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
501 //echild.dom.removeAttribute('xtype');
503 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504 Roo.debug && Roo.log(self_cntr_el);
505 Roo.debug && Roo.log(echild);
506 Roo.debug && Roo.log(cn);
512 // if object has flexy:if - then it may or may not be rendered.
513 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
514 // skip a flexy if element.
515 Roo.debug && Roo.log('skipping render');
516 Roo.debug && Roo.log(tree);
518 Roo.debug && Roo.log('skipping all children');
519 skip_children = true;
524 // actually if flexy:foreach is found, we really want to create
525 // multiple copies here...
527 //Roo.log(this[cntr]());
528 // some elements do not have render methods.. like the layouts...
530 if(this[cntr](true) === false){
535 cn.render && cn.render(this[cntr](true));
538 // then add the element..
545 if (typeof (tree.menu) != 'undefined') {
546 tree.menu.parentType = cn.xtype;
547 tree.menu.triggerEl = cn.el;
548 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
552 if (!tree.items || !tree.items.length) {
554 //Roo.log(["no children", this]);
559 var items = tree.items;
562 //Roo.log(items.length);
564 if (!skip_children) {
565 for(var i =0;i < items.length;i++) {
566 // Roo.log(['add child', items[i]]);
567 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
573 //Roo.log("fire childrenrendered");
575 cn.fireEvent('childrenrendered', this);
581 * Set the element that will be used to show or hide
583 setVisibilityEl : function(el)
585 this.visibilityEl = el;
589 * Get the element that will be used to show or hide
591 getVisibilityEl : function()
593 if (typeof(this.visibilityEl) == 'object') {
594 return this.visibilityEl;
597 if (typeof(this.visibilityEl) == 'string') {
598 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
605 * Show a component - removes 'hidden' class
609 if(!this.getVisibilityEl()){
613 this.getVisibilityEl().removeClass(['hidden','d-none']);
615 this.fireEvent('show', this);
620 * Hide a component - adds 'hidden' class
624 if(!this.getVisibilityEl()){
628 this.getVisibilityEl().addClass(['hidden','d-none']);
630 this.fireEvent('hide', this);
643 * @class Roo.bootstrap.Element
644 * @extends Roo.bootstrap.Component
645 * Bootstrap Element class
646 * @cfg {String} html contents of the element
647 * @cfg {String} tag tag of the element
648 * @cfg {String} cls class of the element
649 * @cfg {Boolean} preventDefault (true|false) default false
650 * @cfg {Boolean} clickable (true|false) default false
653 * Create a new Element
654 * @param {Object} config The config object
657 Roo.bootstrap.Element = function(config){
658 Roo.bootstrap.Element.superclass.constructor.call(this, config);
664 * When a element is chick
665 * @param {Roo.bootstrap.Element} this
666 * @param {Roo.EventObject} e
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
677 preventDefault: false,
680 getAutoCreate : function(){
684 // cls: this.cls, double assign in parent class Component.js :: onRender
691 initEvents: function()
693 Roo.bootstrap.Element.superclass.initEvents.call(this);
696 this.el.on('click', this.onClick, this);
701 onClick : function(e)
703 if(this.preventDefault){
707 this.fireEvent('click', this, e);
710 getValue : function()
712 return this.el.dom.innerHTML;
715 setValue : function(value)
717 this.el.dom.innerHTML = value;
732 * @class Roo.bootstrap.DropTarget
733 * @extends Roo.bootstrap.Element
734 * Bootstrap DropTarget class
736 * @cfg {string} name dropable name
739 * Create a new Dropable Area
740 * @param {Object} config The config object
743 Roo.bootstrap.DropTarget = function(config){
744 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
750 * When a element is chick
751 * @param {Roo.bootstrap.Element} this
752 * @param {Roo.EventObject} e
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
761 getAutoCreate : function(){
766 initEvents: function()
768 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772 drop : this.dragDrop.createDelegate(this),
773 enter : this.dragEnter.createDelegate(this),
774 out : this.dragOut.createDelegate(this),
775 over : this.dragOver.createDelegate(this)
779 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782 dragDrop : function(source,e,data)
784 // user has to decide how to impliment this.
787 //this.fireEvent('drop', this, source, e ,data);
791 dragEnter : function(n, dd, e, data)
793 // probably want to resize the element to match the dropped element..
795 this.originalSize = this.el.getSize();
796 this.el.setSize( n.el.getSize());
797 this.dropZone.DDM.refreshCache(this.name);
798 Roo.log([n, dd, e, data]);
801 dragOut : function(value)
803 // resize back to normal
805 this.el.setSize(this.originalSize);
806 this.dropZone.resetConstraints();
809 dragOver : function()
826 * @class Roo.bootstrap.Body
827 * @extends Roo.bootstrap.Component
828 * Bootstrap Body class
832 * @param {Object} config The config object
835 Roo.bootstrap.Body = function(config){
837 config = config || {};
839 Roo.bootstrap.Body.superclass.constructor.call(this, config);
840 this.el = Roo.get(config.el ? config.el : document.body );
841 if (this.cls && this.cls.length) {
842 Roo.get(document.body).addClass(this.cls);
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
848 is_body : true,// just to make sure it's constructed?
853 onRender : function(ct, position)
855 /* Roo.log("Roo.bootstrap.Body - onRender");
856 if (this.cls && this.cls.length) {
857 Roo.get(document.body).addClass(this.cls);
876 * @class Roo.bootstrap.ButtonGroup
877 * @extends Roo.bootstrap.Component
878 * Bootstrap ButtonGroup class
879 * @cfg {String} size lg | sm | xs (default empty normal)
880 * @cfg {String} align vertical | justified (default none)
881 * @cfg {String} direction up | down (default down)
882 * @cfg {Boolean} toolbar false | true
883 * @cfg {Boolean} btn true | false
888 * @param {Object} config The config object
891 Roo.bootstrap.ButtonGroup = function(config){
892 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
903 getAutoCreate : function(){
909 cfg.html = this.html || cfg.html;
920 if (['vertical','justified'].indexOf(this.align)!==-1) {
921 cfg.cls = 'btn-group-' + this.align;
923 if (this.align == 'justified') {
924 console.log(this.items);
928 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929 cfg.cls += ' btn-group-' + this.size;
932 if (this.direction == 'up') {
933 cfg.cls += ' dropup' ;
939 * Add a button to the group (similar to NavItem API.)
941 addItem : function(cfg)
943 var cn = new Roo.bootstrap.Button(cfg);
945 cn.parentId = this.id;
946 cn.onRender(this.el, null);
960 * @class Roo.bootstrap.Button
961 * @extends Roo.bootstrap.Component
962 * Bootstrap Button class
963 * @cfg {String} html The button content
964 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
965 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
966 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967 * @cfg {String} size ( lg | sm | xs)
968 * @cfg {String} tag ( a | input | submit)
969 * @cfg {String} href empty or href
970 * @cfg {Boolean} disabled default false;
971 * @cfg {Boolean} isClose default false;
972 * @cfg {String} glyphicon depricated - use fa
973 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974 * @cfg {String} badge text for badge
975 * @cfg {String} theme (default|glow)
976 * @cfg {Boolean} inverse dark themed version
977 * @cfg {Boolean} toggle is it a slidy toggle button
978 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
979 * @cfg {String} ontext text for on slidy toggle state
980 * @cfg {String} offtext text for off slidy toggle state
981 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
982 * @cfg {Boolean} removeClass remove the standard class..
983 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
986 * Create a new button
987 * @param {Object} config The config object
991 Roo.bootstrap.Button = function(config){
992 Roo.bootstrap.Button.superclass.constructor.call(this, config);
993 this.weightClass = ["btn-default btn-outline-secondary",
1005 * When a butotn is pressed
1006 * @param {Roo.bootstrap.Button} btn
1007 * @param {Roo.EventObject} e
1012 * After the button has been toggles
1013 * @param {Roo.bootstrap.Button} btn
1014 * @param {Roo.EventObject} e
1015 * @param {boolean} pressed (also available as button.pressed)
1021 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1042 preventDefault: true,
1050 getAutoCreate : function(){
1058 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1059 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1060 this.tag = 'button';
1064 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1066 if (this.toggle == true) {
1069 cls: 'slider-frame roo-button',
1073 'data-on-text':'ON',
1074 'data-off-text':'OFF',
1075 cls: 'slider-button',
1081 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1082 cfg.cls += ' '+this.weight;
1089 cfg.cls += ' close';
1091 cfg["aria-hidden"] = true;
1093 cfg.html = "×";
1099 if (this.theme==='default') {
1100 cfg.cls = 'btn roo-button';
1102 //if (this.parentType != 'Navbar') {
1103 this.weight = this.weight.length ? this.weight : 'default';
1105 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1107 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1108 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1109 cfg.cls += ' btn-' + outline + weight;
1110 if (this.weight == 'default') {
1112 cfg.cls += ' btn-' + this.weight;
1115 } else if (this.theme==='glow') {
1118 cfg.cls = 'btn-glow roo-button';
1120 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1122 cfg.cls += ' ' + this.weight;
1128 this.cls += ' inverse';
1132 if (this.active || this.pressed === true) {
1133 cfg.cls += ' active';
1136 if (this.disabled) {
1137 cfg.disabled = 'disabled';
1141 Roo.log('changing to ul' );
1143 this.glyphicon = 'caret';
1144 if (Roo.bootstrap.version == 4) {
1145 this.fa = 'caret-down';
1150 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1152 //gsRoo.log(this.parentType);
1153 if (this.parentType === 'Navbar' && !this.parent().bar) {
1154 Roo.log('changing to li?');
1163 href : this.href || '#'
1166 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1167 cfg.cls += ' dropdown';
1174 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1176 if (this.glyphicon) {
1177 cfg.html = ' ' + cfg.html;
1182 cls: 'glyphicon glyphicon-' + this.glyphicon
1187 cfg.html = ' ' + cfg.html;
1192 cls: 'fa fas fa-' + this.fa
1202 // cfg.cls='btn roo-button';
1206 var value = cfg.html;
1211 cls: 'glyphicon glyphicon-' + this.glyphicon,
1218 cls: 'fa fas fa-' + this.fa,
1223 var bw = this.badge_weight.length ? this.badge_weight :
1224 (this.weight.length ? this.weight : 'secondary');
1225 bw = bw == 'default' ? 'secondary' : bw;
1231 cls: 'badge badge-' + bw,
1240 cfg.cls += ' dropdown';
1241 cfg.html = typeof(cfg.html) != 'undefined' ?
1242 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1245 if (cfg.tag !== 'a' && this.href !== '') {
1246 throw "Tag must be a to set href.";
1247 } else if (this.href.length > 0) {
1248 cfg.href = this.href;
1251 if(this.removeClass){
1256 cfg.target = this.target;
1261 initEvents: function() {
1262 // Roo.log('init events?');
1263 // Roo.log(this.el.dom);
1266 if (typeof (this.menu) != 'undefined') {
1267 this.menu.parentType = this.xtype;
1268 this.menu.triggerEl = this.el;
1269 this.addxtype(Roo.apply({}, this.menu));
1273 if (this.el.hasClass('roo-button')) {
1274 this.el.on('click', this.onClick, this);
1276 this.el.select('.roo-button').on('click', this.onClick, this);
1279 if(this.removeClass){
1280 this.el.on('click', this.onClick, this);
1283 this.el.enableDisplayMode();
1286 onClick : function(e)
1288 if (this.disabled) {
1292 Roo.log('button on click ');
1293 if(this.preventDefault){
1297 if (this.pressed === true || this.pressed === false) {
1298 this.toggleActive(e);
1302 this.fireEvent('click', this, e);
1306 * Enables this button
1310 this.disabled = false;
1311 this.el.removeClass('disabled');
1315 * Disable this button
1317 disable : function()
1319 this.disabled = true;
1320 this.el.addClass('disabled');
1323 * sets the active state on/off,
1324 * @param {Boolean} state (optional) Force a particular state
1326 setActive : function(v) {
1328 this.el[v ? 'addClass' : 'removeClass']('active');
1332 * toggles the current active state
1334 toggleActive : function(e)
1336 this.setActive(!this.pressed);
1337 this.fireEvent('toggle', this, e, !this.pressed);
1340 * get the current active state
1341 * @return {boolean} true if it's active
1343 isActive : function()
1345 return this.el.hasClass('active');
1348 * set the text of the first selected button
1350 setText : function(str)
1352 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1355 * get the text of the first selected button
1357 getText : function()
1359 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1362 setWeight : function(str)
1364 this.el.removeClass(this.weightClass);
1366 var outline = this.outline ? 'outline-' : '';
1367 if (str == 'default') {
1368 this.el.addClass('btn-default btn-outline-secondary');
1371 this.el.addClass('btn-' + outline + str);
1385 * @class Roo.bootstrap.Column
1386 * @extends Roo.bootstrap.Component
1387 * Bootstrap Column class
1388 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1389 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1390 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1391 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1392 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1393 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1394 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1395 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1398 * @cfg {Boolean} hidden (true|false) hide the element
1399 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1400 * @cfg {String} fa (ban|check|...) font awesome icon
1401 * @cfg {Number} fasize (1|2|....) font awsome size
1403 * @cfg {String} icon (info-sign|check|...) glyphicon name
1405 * @cfg {String} html content of column.
1408 * Create a new Column
1409 * @param {Object} config The config object
1412 Roo.bootstrap.Column = function(config){
1413 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1416 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1434 getAutoCreate : function(){
1435 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1443 var sizes = ['xs','sm','md','lg'];
1444 sizes.map(function(size ,ix){
1445 //Roo.log( size + ':' + settings[size]);
1447 if (settings[size+'off'] !== false) {
1448 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1451 if (settings[size] === false) {
1455 if (!settings[size]) { // 0 = hidden
1456 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1458 for (var i = ix; i > -1; i--) {
1459 cfg.cls += ' d-' + sizes[i] + '-none';
1465 cfg.cls += ' col-' + size + '-' + settings[size] + (
1466 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1472 cfg.cls += ' hidden';
1475 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1476 cfg.cls +=' alert alert-' + this.alert;
1480 if (this.html.length) {
1481 cfg.html = this.html;
1485 if (this.fasize > 1) {
1486 fasize = ' fa-' + this.fasize + 'x';
1488 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1493 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1512 * @class Roo.bootstrap.Container
1513 * @extends Roo.bootstrap.Component
1514 * Bootstrap Container class
1515 * @cfg {Boolean} jumbotron is it a jumbotron element
1516 * @cfg {String} html content of element
1517 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1518 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1519 * @cfg {String} header content of header (for panel)
1520 * @cfg {String} footer content of footer (for panel)
1521 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1522 * @cfg {String} tag (header|aside|section) type of HTML tag.
1523 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1524 * @cfg {String} fa font awesome icon
1525 * @cfg {String} icon (info-sign|check|...) glyphicon name
1526 * @cfg {Boolean} hidden (true|false) hide the element
1527 * @cfg {Boolean} expandable (true|false) default false
1528 * @cfg {Boolean} expanded (true|false) default true
1529 * @cfg {String} rheader contet on the right of header
1530 * @cfg {Boolean} clickable (true|false) default false
1534 * Create a new Container
1535 * @param {Object} config The config object
1538 Roo.bootstrap.Container = function(config){
1539 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1545 * After the panel has been expand
1547 * @param {Roo.bootstrap.Container} this
1552 * After the panel has been collapsed
1554 * @param {Roo.bootstrap.Container} this
1559 * When a element is chick
1560 * @param {Roo.bootstrap.Container} this
1561 * @param {Roo.EventObject} e
1567 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1585 getChildContainer : function() {
1591 if (this.panel.length) {
1592 return this.el.select('.panel-body',true).first();
1599 getAutoCreate : function(){
1602 tag : this.tag || 'div',
1606 if (this.jumbotron) {
1607 cfg.cls = 'jumbotron';
1612 // - this is applied by the parent..
1614 // cfg.cls = this.cls + '';
1617 if (this.sticky.length) {
1619 var bd = Roo.get(document.body);
1620 if (!bd.hasClass('bootstrap-sticky')) {
1621 bd.addClass('bootstrap-sticky');
1622 Roo.select('html',true).setStyle('height', '100%');
1625 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1629 if (this.well.length) {
1630 switch (this.well) {
1633 cfg.cls +=' well well-' +this.well;
1642 cfg.cls += ' hidden';
1646 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1647 cfg.cls +=' alert alert-' + this.alert;
1652 if (this.panel.length) {
1653 cfg.cls += ' panel panel-' + this.panel;
1655 if (this.header.length) {
1659 if(this.expandable){
1661 cfg.cls = cfg.cls + ' expandable';
1665 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1673 cls : 'panel-title',
1674 html : (this.expandable ? ' ' : '') + this.header
1678 cls: 'panel-header-right',
1684 cls : 'panel-heading',
1685 style : this.expandable ? 'cursor: pointer' : '',
1693 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1698 if (this.footer.length) {
1700 cls : 'panel-footer',
1709 body.html = this.html || cfg.html;
1710 // prefix with the icons..
1712 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1715 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1720 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1721 cfg.cls = 'container';
1727 initEvents: function()
1729 if(this.expandable){
1730 var headerEl = this.headerEl();
1733 headerEl.on('click', this.onToggleClick, this);
1738 this.el.on('click', this.onClick, this);
1743 onToggleClick : function()
1745 var headerEl = this.headerEl();
1761 if(this.fireEvent('expand', this)) {
1763 this.expanded = true;
1765 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1767 this.el.select('.panel-body',true).first().removeClass('hide');
1769 var toggleEl = this.toggleEl();
1775 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1780 collapse : function()
1782 if(this.fireEvent('collapse', this)) {
1784 this.expanded = false;
1786 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1787 this.el.select('.panel-body',true).first().addClass('hide');
1789 var toggleEl = this.toggleEl();
1795 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1799 toggleEl : function()
1801 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1805 return this.el.select('.panel-heading .fa',true).first();
1808 headerEl : function()
1810 if(!this.el || !this.panel.length || !this.header.length){
1814 return this.el.select('.panel-heading',true).first()
1819 if(!this.el || !this.panel.length){
1823 return this.el.select('.panel-body',true).first()
1826 titleEl : function()
1828 if(!this.el || !this.panel.length || !this.header.length){
1832 return this.el.select('.panel-title',true).first();
1835 setTitle : function(v)
1837 var titleEl = this.titleEl();
1843 titleEl.dom.innerHTML = v;
1846 getTitle : function()
1849 var titleEl = this.titleEl();
1855 return titleEl.dom.innerHTML;
1858 setRightTitle : function(v)
1860 var t = this.el.select('.panel-header-right',true).first();
1866 t.dom.innerHTML = v;
1869 onClick : function(e)
1873 this.fireEvent('click', this, e);
1880 * This is BS4's Card element.. - similar to our containers probably..
1884 * @class Roo.bootstrap.Card
1885 * @extends Roo.bootstrap.Component
1886 * Bootstrap Card class
1889 * possible... may not be implemented..
1890 * @cfg {String} header_image src url of image.
1891 * @cfg {String|Object} header
1892 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1894 * @cfg {String} title
1895 * @cfg {String} subtitle
1896 * @cfg {String} html -- html contents - or just use children..
1897 * @cfg {String} footer
1899 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1901 * @cfg {String} margin (0|1|2|3|4|5|auto)
1902 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1903 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1904 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1905 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1906 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1907 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1909 * @cfg {String} padding (0|1|2|3|4|5)
1910 * @cfg {String} padding_top (0|1|2|3|4|5)
1911 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1912 * @cfg {String} padding_left (0|1|2|3|4|5)
1913 * @cfg {String} padding_right (0|1|2|3|4|5)
1914 * @cfg {String} padding_x (0|1|2|3|4|5)
1915 * @cfg {String} padding_y (0|1|2|3|4|5)
1917 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1918 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1919 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1920 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1921 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1923 * @config {Boolean} dragable if this card can be dragged.
1924 * @config {String} drag_group group for drag
1925 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1926 * @config {String} drop_group group for drag
1928 * @config {Boolean} collapsable can the body be collapsed.
1929 * @config {Boolean} collapsed is the body collapsed when rendered...
1931 * Create a new Container
1932 * @param {Object} config The config object
1935 Roo.bootstrap.Card = function(config){
1936 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1944 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1949 margin: '', /// may be better in component?
1979 collapsable : false,
1986 childContainer : false,
1987 dropEl : false, /// the dom placeholde element that indicates drop location.
1989 layoutCls : function()
1993 Roo.log(this.margin_bottom.length);
1994 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1995 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1997 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1998 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2000 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2001 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2005 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2006 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2007 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2011 // more generic support?
2019 // Roo.log("Call onRender: " + this.xtype);
2020 /* We are looking at something like this.
2022 <img src="..." class="card-img-top" alt="...">
2023 <div class="card-body">
2024 <h5 class="card-title">Card title</h5>
2025 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2027 >> this bit is really the body...
2028 <div> << we will ad dthis in hopefully it will not break shit.
2030 ** card text does not actually have any styling...
2032 <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>
2035 <a href="#" class="card-link">Card link</a>
2038 <div class="card-footer">
2039 <small class="text-muted">Last updated 3 mins ago</small>
2043 getAutoCreate : function(){
2051 if (this.weight.length && this.weight != 'light') {
2052 cfg.cls += ' text-white';
2054 cfg.cls += ' text-dark'; // need as it's nested..
2056 if (this.weight.length) {
2057 cfg.cls += ' bg-' + this.weight;
2060 cfg.cls += this.layoutCls();
2063 if (this.header.length) {
2065 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2066 cls : 'card-header',
2074 cls : 'card-header d-none',
2079 if (this.collapsable) {
2082 cls : 'd-block user-select-none',
2086 cls : 'roo-collapse-toggle fa fa-chevron-down float-right'
2091 hdr.cn.push(hdr_ctr);
2093 if (this.header.length) {
2096 cls: 'roo-card-header-ctr',
2101 if (this.header_image.length) {
2104 cls : 'card-img-top',
2105 src: this.header_image // escape?
2110 cls : 'card-img-top d-none'
2120 if (this.collapsable) {
2123 cls : 'roo-collapsable collapse ' + (this.collapsed ? '' : 'show'),
2130 if (this.title.length) {
2134 src: this.title // escape?
2138 if (this.subtitle.length) {
2142 src: this.subtitle // escape?
2148 cls : 'roo-card-body-ctr'
2151 if (this.html.length) {
2157 // fixme ? handle objects?
2158 if (this.footer.length) {
2161 cls : 'card-footer',
2162 html: this.footer // escape?
2171 getCardHeader : function()
2173 var ret = this.el.select('.card-header',true).first();
2174 if (ret.hasClass('d-none')) {
2175 ret.removeClass('d-none');
2181 getCardImageTop : function()
2183 var ret = this.el.select('.card-img-top',true).first();
2184 if (ret.hasClass('d-none')) {
2185 ret.removeClass('d-none');
2191 getChildContainer : function()
2197 return this.el.select('.roo-card-body-ctr',true).first();
2200 initEvents: function()
2203 this.bodyEl = this.getChildContainer();
2205 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2206 containerScroll: true,
2207 ddGroup: this.drag_group || 'default_card_drag_group'
2209 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2211 if (this.dropable) {
2212 this.dropZone = new Roo.dd.DropZone(this.getChildContainer(), {
2213 containerScroll: true,
2214 ddGroup: this.drop_group || 'default_card_drag_group'
2216 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2217 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2218 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2219 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2220 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2223 if (this.collapsable) {
2224 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2227 getDragData : function(e) {
2228 var target = this.getEl();
2230 //this.handleSelection(e);
2235 nodes: this.getEl(),
2240 dragData.ddel = target.dom ; // the div element
2241 Roo.log(target.getWidth( ));
2242 dragData.ddel.style.width = target.getWidth() + 'px';
2249 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2250 * whole Element becomes the target, and this causes the drop gesture to append.
2252 getTargetFromEvent : function(e)
2254 var target = e.getTarget();
2255 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2256 target = target.parentNode;
2258 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2259 // see if target is one of the 'cards'...
2260 var ctarget = false;
2262 //Roo.log(this.items.length);
2263 var lpos = pos = false;
2264 for (var i = 0;i< this.items.length;i++) {
2266 if (!this.items[i].el.hasClass('card')) {
2269 pos = this.getDropPoint(e, this.items[i].el.dom);
2271 //Roo.log(this.items[i].el.dom.id);
2272 cards.push(this.items[i]);
2273 if (pos == 'above') {
2274 ctarget = this.items[i > 0 ? i-1 : 0];
2275 pos = i > 0 ? 'below' : pos;
2281 ctarget = cards[cards.length-1] || this.el.dom;
2286 //Roo.log(['getTargetFromEvent', ctarget]);
2287 return [ ctarget, pos ];
2290 onNodeEnter : function(n, dd, e, data){
2293 onNodeOver : function(n, dd, e, data)
2295 // Roo.log(['onNodeOver'])
2297 var pt = this.getDropPoint(e, n, dd);
2298 // set the insert point style on the target node
2299 //var dragElClass = this.dropNotAllowed;
2301 this.dropPlaceHolder('hide');
2306 target_info = this.getTargetFromEvent(e);
2307 Roo.log(['getTargetFromEvent', target_info[0].el.dom.id,target_info[1]]);
2310 this.dropPlaceHolder('show', targetinfo,data);
2311 return false; //dragElClass;
2313 onNodeOut : function(n, dd, e, data){
2314 //this.removeDropIndicators(n);
2316 onNodeDrop : function(n, dd, e, data)
2320 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2323 var pt = this.getDropPoint(e, n, dd);
2324 var insertAt = (n == this.bodyEl.dom) ? this.items.length : n.nodeIndex;
2325 if (pt == "below") {
2328 for (var i = 0; i < this.items.length; i++) {
2329 var r = this.items[i];
2330 //var dup = this.store.getById(r.id);
2331 if (dup && (dd != this.dragZone)) {
2332 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
2335 this.store.insert(insertAt++, r.copy());
2337 data.source.isDirtyFlag = true;
2339 this.store.insert(insertAt++, r);
2341 this.isDirtyFlag = true;
2344 this.dragZone.cachedTarget = null;
2348 /** Decide whether to drop above or below a View node. */
2349 getDropPoint : function(e, n, dd)
2354 if (n == this.bodyEl.dom) {
2357 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2358 var c = t + (b - t) / 2;
2359 var y = Roo.lib.Event.getPageY(e);
2366 onToggleCollapse : function(e)
2368 if (this.collapsed) {
2369 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2370 this.el.select('.roo-collapsable').addClass('show');
2371 this.collapsed = false;
2374 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2375 this.el.select('.roo-collapsable').removeClass('show');
2376 this.collapsed = true;
2380 dropPlaceHolder: function (action, where_ar, data)
2382 if (this.dropEl === false) {
2383 this.dropEl = new Roo.DomHelper.append(this.dom.bodyEl, {
2388 if (action == 'hide') {
2389 this.dropEl.removeClass(['d-none', 'd-block']);
2390 this.dropEl.addClass('d-none');
2393 var cardel = where_ar[0].el.dom;
2395 this.dropEl.parentNode.removeChild(this.dropEl);
2396 if (where_ar[0] == 'before') {
2397 this.cardel.parentNode.insertBefore(this.dropEl, cardel);
2398 } else if (cardel.nextSibling) {
2399 this.cardel.parentNode.insertBefore(this.dropEl,ns);
2401 this.cardel.parentNode.append(this.dropEl);
2403 this.cardel.style.width = "100%";
2404 this.cardel.style.height = Roo.get(data.ddel).getHeight() + 'px';
2418 * Card header - holder for the card header elements.
2423 * @class Roo.bootstrap.CardHeader
2424 * @extends Roo.bootstrap.Element
2425 * Bootstrap CardHeader class
2427 * Create a new Card Header - that you can embed children into
2428 * @param {Object} config The config object
2431 Roo.bootstrap.CardHeader = function(config){
2432 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2435 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2438 container_method : 'getCardHeader'
2451 * Card header - holder for the card header elements.
2456 * @class Roo.bootstrap.CardImageTop
2457 * @extends Roo.bootstrap.Element
2458 * Bootstrap CardImageTop class
2460 * Create a new Card Image Top container
2461 * @param {Object} config The config object
2464 Roo.bootstrap.CardImageTop = function(config){
2465 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2468 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2471 container_method : 'getCardImageTop'
2489 * @class Roo.bootstrap.Img
2490 * @extends Roo.bootstrap.Component
2491 * Bootstrap Img class
2492 * @cfg {Boolean} imgResponsive false | true
2493 * @cfg {String} border rounded | circle | thumbnail
2494 * @cfg {String} src image source
2495 * @cfg {String} alt image alternative text
2496 * @cfg {String} href a tag href
2497 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2498 * @cfg {String} xsUrl xs image source
2499 * @cfg {String} smUrl sm image source
2500 * @cfg {String} mdUrl md image source
2501 * @cfg {String} lgUrl lg image source
2504 * Create a new Input
2505 * @param {Object} config The config object
2508 Roo.bootstrap.Img = function(config){
2509 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2515 * The img click event for the img.
2516 * @param {Roo.EventObject} e
2522 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2524 imgResponsive: true,
2534 getAutoCreate : function()
2536 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2537 return this.createSingleImg();
2542 cls: 'roo-image-responsive-group',
2547 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2549 if(!_this[size + 'Url']){
2555 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2556 html: _this.html || cfg.html,
2557 src: _this[size + 'Url']
2560 img.cls += ' roo-image-responsive-' + size;
2562 var s = ['xs', 'sm', 'md', 'lg'];
2564 s.splice(s.indexOf(size), 1);
2566 Roo.each(s, function(ss){
2567 img.cls += ' hidden-' + ss;
2570 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2571 cfg.cls += ' img-' + _this.border;
2575 cfg.alt = _this.alt;
2588 a.target = _this.target;
2592 cfg.cn.push((_this.href) ? a : img);
2599 createSingleImg : function()
2603 cls: (this.imgResponsive) ? 'img-responsive' : '',
2605 src : 'about:blank' // just incase src get's set to undefined?!?
2608 cfg.html = this.html || cfg.html;
2610 cfg.src = this.src || cfg.src;
2612 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2613 cfg.cls += ' img-' + this.border;
2630 a.target = this.target;
2635 return (this.href) ? a : cfg;
2638 initEvents: function()
2641 this.el.on('click', this.onClick, this);
2646 onClick : function(e)
2648 Roo.log('img onclick');
2649 this.fireEvent('click', this, e);
2652 * Sets the url of the image - used to update it
2653 * @param {String} url the url of the image
2656 setSrc : function(url)
2660 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2661 this.el.dom.src = url;
2665 this.el.select('img', true).first().dom.src = url;
2681 * @class Roo.bootstrap.Link
2682 * @extends Roo.bootstrap.Component
2683 * Bootstrap Link Class
2684 * @cfg {String} alt image alternative text
2685 * @cfg {String} href a tag href
2686 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2687 * @cfg {String} html the content of the link.
2688 * @cfg {String} anchor name for the anchor link
2689 * @cfg {String} fa - favicon
2691 * @cfg {Boolean} preventDefault (true | false) default false
2695 * Create a new Input
2696 * @param {Object} config The config object
2699 Roo.bootstrap.Link = function(config){
2700 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2706 * The img click event for the img.
2707 * @param {Roo.EventObject} e
2713 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2717 preventDefault: false,
2723 getAutoCreate : function()
2725 var html = this.html || '';
2727 if (this.fa !== false) {
2728 html = '<i class="fa fa-' + this.fa + '"></i>';
2733 // anchor's do not require html/href...
2734 if (this.anchor === false) {
2736 cfg.href = this.href || '#';
2738 cfg.name = this.anchor;
2739 if (this.html !== false || this.fa !== false) {
2742 if (this.href !== false) {
2743 cfg.href = this.href;
2747 if(this.alt !== false){
2752 if(this.target !== false) {
2753 cfg.target = this.target;
2759 initEvents: function() {
2761 if(!this.href || this.preventDefault){
2762 this.el.on('click', this.onClick, this);
2766 onClick : function(e)
2768 if(this.preventDefault){
2771 //Roo.log('img onclick');
2772 this.fireEvent('click', this, e);
2785 * @class Roo.bootstrap.Header
2786 * @extends Roo.bootstrap.Component
2787 * Bootstrap Header class
2788 * @cfg {String} html content of header
2789 * @cfg {Number} level (1|2|3|4|5|6) default 1
2792 * Create a new Header
2793 * @param {Object} config The config object
2797 Roo.bootstrap.Header = function(config){
2798 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2801 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2809 getAutoCreate : function(){
2814 tag: 'h' + (1 *this.level),
2815 html: this.html || ''
2827 * Ext JS Library 1.1.1
2828 * Copyright(c) 2006-2007, Ext JS, LLC.
2830 * Originally Released Under LGPL - original licence link has changed is not relivant.
2833 * <script type="text/javascript">
2837 * @class Roo.bootstrap.MenuMgr
2838 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2841 Roo.bootstrap.MenuMgr = function(){
2842 var menus, active, groups = {}, attached = false, lastShow = new Date();
2844 // private - called when first menu is created
2847 active = new Roo.util.MixedCollection();
2848 Roo.get(document).addKeyListener(27, function(){
2849 if(active.length > 0){
2857 if(active && active.length > 0){
2858 var c = active.clone();
2868 if(active.length < 1){
2869 Roo.get(document).un("mouseup", onMouseDown);
2877 var last = active.last();
2878 lastShow = new Date();
2881 Roo.get(document).on("mouseup", onMouseDown);
2886 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2887 m.parentMenu.activeChild = m;
2888 }else if(last && last.isVisible()){
2889 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2894 function onBeforeHide(m){
2896 m.activeChild.hide();
2898 if(m.autoHideTimer){
2899 clearTimeout(m.autoHideTimer);
2900 delete m.autoHideTimer;
2905 function onBeforeShow(m){
2906 var pm = m.parentMenu;
2907 if(!pm && !m.allowOtherMenus){
2909 }else if(pm && pm.activeChild && active != m){
2910 pm.activeChild.hide();
2914 // private this should really trigger on mouseup..
2915 function onMouseDown(e){
2916 Roo.log("on Mouse Up");
2918 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2919 Roo.log("MenuManager hideAll");
2928 function onBeforeCheck(mi, state){
2930 var g = groups[mi.group];
2931 for(var i = 0, l = g.length; i < l; i++){
2933 g[i].setChecked(false);
2942 * Hides all menus that are currently visible
2944 hideAll : function(){
2949 register : function(menu){
2953 menus[menu.id] = menu;
2954 menu.on("beforehide", onBeforeHide);
2955 menu.on("hide", onHide);
2956 menu.on("beforeshow", onBeforeShow);
2957 menu.on("show", onShow);
2959 if(g && menu.events["checkchange"]){
2963 groups[g].push(menu);
2964 menu.on("checkchange", onCheck);
2969 * Returns a {@link Roo.menu.Menu} object
2970 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2971 * be used to generate and return a new Menu instance.
2973 get : function(menu){
2974 if(typeof menu == "string"){ // menu id
2976 }else if(menu.events){ // menu instance
2979 /*else if(typeof menu.length == 'number'){ // array of menu items?
2980 return new Roo.bootstrap.Menu({items:menu});
2981 }else{ // otherwise, must be a config
2982 return new Roo.bootstrap.Menu(menu);
2989 unregister : function(menu){
2990 delete menus[menu.id];
2991 menu.un("beforehide", onBeforeHide);
2992 menu.un("hide", onHide);
2993 menu.un("beforeshow", onBeforeShow);
2994 menu.un("show", onShow);
2996 if(g && menu.events["checkchange"]){
2997 groups[g].remove(menu);
2998 menu.un("checkchange", onCheck);
3003 registerCheckable : function(menuItem){
3004 var g = menuItem.group;
3009 groups[g].push(menuItem);
3010 menuItem.on("beforecheckchange", onBeforeCheck);
3015 unregisterCheckable : function(menuItem){
3016 var g = menuItem.group;
3018 groups[g].remove(menuItem);
3019 menuItem.un("beforecheckchange", onBeforeCheck);
3031 * @class Roo.bootstrap.Menu
3032 * @extends Roo.bootstrap.Component
3033 * Bootstrap Menu class - container for MenuItems
3034 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3035 * @cfg {bool} hidden if the menu should be hidden when rendered.
3036 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3037 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3041 * @param {Object} config The config object
3045 Roo.bootstrap.Menu = function(config){
3046 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3047 if (this.registerMenu && this.type != 'treeview') {
3048 Roo.bootstrap.MenuMgr.register(this);
3055 * Fires before this menu is displayed (return false to block)
3056 * @param {Roo.menu.Menu} this
3061 * Fires before this menu is hidden (return false to block)
3062 * @param {Roo.menu.Menu} this
3067 * Fires after this menu is displayed
3068 * @param {Roo.menu.Menu} this
3073 * Fires after this menu is hidden
3074 * @param {Roo.menu.Menu} this
3079 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3080 * @param {Roo.menu.Menu} this
3081 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3082 * @param {Roo.EventObject} e
3087 * Fires when the mouse is hovering over this menu
3088 * @param {Roo.menu.Menu} this
3089 * @param {Roo.EventObject} e
3090 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3095 * Fires when the mouse exits this menu
3096 * @param {Roo.menu.Menu} this
3097 * @param {Roo.EventObject} e
3098 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3103 * Fires when a menu item contained in this menu is clicked
3104 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3105 * @param {Roo.EventObject} e
3109 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3112 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3116 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3119 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3121 registerMenu : true,
3123 menuItems :false, // stores the menu items..
3133 getChildContainer : function() {
3137 getAutoCreate : function(){
3139 //if (['right'].indexOf(this.align)!==-1) {
3140 // cfg.cn[1].cls += ' pull-right'
3146 cls : 'dropdown-menu' ,
3147 style : 'z-index:1000'
3151 if (this.type === 'submenu') {
3152 cfg.cls = 'submenu active';
3154 if (this.type === 'treeview') {
3155 cfg.cls = 'treeview-menu';
3160 initEvents : function() {
3162 // Roo.log("ADD event");
3163 // Roo.log(this.triggerEl.dom);
3165 this.triggerEl.on('click', this.onTriggerClick, this);
3167 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3170 if (this.triggerEl.hasClass('nav-item')) {
3171 // dropdown toggle on the 'a' in BS4?
3172 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3174 this.triggerEl.addClass('dropdown-toggle');
3177 this.el.on('touchstart' , this.onTouch, this);
3179 this.el.on('click' , this.onClick, this);
3181 this.el.on("mouseover", this.onMouseOver, this);
3182 this.el.on("mouseout", this.onMouseOut, this);
3186 findTargetItem : function(e)
3188 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3192 //Roo.log(t); Roo.log(t.id);
3194 //Roo.log(this.menuitems);
3195 return this.menuitems.get(t.id);
3197 //return this.items.get(t.menuItemId);
3203 onTouch : function(e)
3205 Roo.log("menu.onTouch");
3206 //e.stopEvent(); this make the user popdown broken
3210 onClick : function(e)
3212 Roo.log("menu.onClick");
3214 var t = this.findTargetItem(e);
3215 if(!t || t.isContainer){
3220 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3221 if(t == this.activeItem && t.shouldDeactivate(e)){
3222 this.activeItem.deactivate();
3223 delete this.activeItem;
3227 this.setActiveItem(t, true);
3235 Roo.log('pass click event');
3239 this.fireEvent("click", this, t, e);
3243 if(!t.href.length || t.href == '#'){
3244 (function() { _this.hide(); }).defer(100);
3249 onMouseOver : function(e){
3250 var t = this.findTargetItem(e);
3253 // if(t.canActivate && !t.disabled){
3254 // this.setActiveItem(t, true);
3258 this.fireEvent("mouseover", this, e, t);
3260 isVisible : function(){
3261 return !this.hidden;
3263 onMouseOut : function(e){
3264 var t = this.findTargetItem(e);
3267 // if(t == this.activeItem && t.shouldDeactivate(e)){
3268 // this.activeItem.deactivate();
3269 // delete this.activeItem;
3272 this.fireEvent("mouseout", this, e, t);
3277 * Displays this menu relative to another element
3278 * @param {String/HTMLElement/Roo.Element} element The element to align to
3279 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3280 * the element (defaults to this.defaultAlign)
3281 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3283 show : function(el, pos, parentMenu)
3285 if (false === this.fireEvent("beforeshow", this)) {
3286 Roo.log("show canceled");
3289 this.parentMenu = parentMenu;
3294 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3297 * Displays this menu at a specific xy position
3298 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3299 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3301 showAt : function(xy, parentMenu, /* private: */_e){
3302 this.parentMenu = parentMenu;
3307 this.fireEvent("beforeshow", this);
3308 //xy = this.el.adjustForConstraints(xy);
3312 this.hideMenuItems();
3313 this.hidden = false;
3314 this.triggerEl.addClass('open');
3315 this.el.addClass('show');
3317 // reassign x when hitting right
3318 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3319 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3322 // reassign y when hitting bottom
3323 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3324 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3327 // but the list may align on trigger left or trigger top... should it be a properity?
3329 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3334 this.fireEvent("show", this);
3340 this.doFocus.defer(50, this);
3344 doFocus : function(){
3346 this.focusEl.focus();
3351 * Hides this menu and optionally all parent menus
3352 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3354 hide : function(deep)
3356 if (false === this.fireEvent("beforehide", this)) {
3357 Roo.log("hide canceled");
3360 this.hideMenuItems();
3361 if(this.el && this.isVisible()){
3363 if(this.activeItem){
3364 this.activeItem.deactivate();
3365 this.activeItem = null;
3367 this.triggerEl.removeClass('open');;
3368 this.el.removeClass('show');
3370 this.fireEvent("hide", this);
3372 if(deep === true && this.parentMenu){
3373 this.parentMenu.hide(true);
3377 onTriggerClick : function(e)
3379 Roo.log('trigger click');
3381 var target = e.getTarget();
3383 Roo.log(target.nodeName.toLowerCase());
3385 if(target.nodeName.toLowerCase() === 'i'){
3391 onTriggerPress : function(e)
3393 Roo.log('trigger press');
3394 //Roo.log(e.getTarget());
3395 // Roo.log(this.triggerEl.dom);
3397 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3398 var pel = Roo.get(e.getTarget());
3399 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3400 Roo.log('is treeview or dropdown?');
3404 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3408 if (this.isVisible()) {
3413 this.show(this.triggerEl, '?', false);
3416 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3423 hideMenuItems : function()
3425 Roo.log("hide Menu Items");
3430 this.el.select('.open',true).each(function(aa) {
3432 aa.removeClass('open');
3436 addxtypeChild : function (tree, cntr) {
3437 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3439 this.menuitems.add(comp);
3451 this.getEl().dom.innerHTML = '';
3452 this.menuitems.clear();
3466 * @class Roo.bootstrap.MenuItem
3467 * @extends Roo.bootstrap.Component
3468 * Bootstrap MenuItem class
3469 * @cfg {String} html the menu label
3470 * @cfg {String} href the link
3471 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3472 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3473 * @cfg {Boolean} active used on sidebars to highlight active itesm
3474 * @cfg {String} fa favicon to show on left of menu item.
3475 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3479 * Create a new MenuItem
3480 * @param {Object} config The config object
3484 Roo.bootstrap.MenuItem = function(config){
3485 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3490 * The raw click event for the entire grid.
3491 * @param {Roo.bootstrap.MenuItem} this
3492 * @param {Roo.EventObject} e
3498 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3502 preventDefault: false,
3503 isContainer : false,
3507 getAutoCreate : function(){
3509 if(this.isContainer){
3512 cls: 'dropdown-menu-item '
3522 cls : 'dropdown-item',
3527 if (this.fa !== false) {
3530 cls : 'fa fa-' + this.fa
3539 cls: 'dropdown-menu-item',
3542 if (this.parent().type == 'treeview') {
3543 cfg.cls = 'treeview-menu';
3546 cfg.cls += ' active';
3551 anc.href = this.href || cfg.cn[0].href ;
3552 ctag.html = this.html || cfg.cn[0].html ;
3556 initEvents: function()
3558 if (this.parent().type == 'treeview') {
3559 this.el.select('a').on('click', this.onClick, this);
3563 this.menu.parentType = this.xtype;
3564 this.menu.triggerEl = this.el;
3565 this.menu = this.addxtype(Roo.apply({}, this.menu));
3569 onClick : function(e)
3571 Roo.log('item on click ');
3573 if(this.preventDefault){
3576 //this.parent().hideMenuItems();
3578 this.fireEvent('click', this, e);
3597 * @class Roo.bootstrap.MenuSeparator
3598 * @extends Roo.bootstrap.Component
3599 * Bootstrap MenuSeparator class
3602 * Create a new MenuItem
3603 * @param {Object} config The config object
3607 Roo.bootstrap.MenuSeparator = function(config){
3608 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3611 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3613 getAutoCreate : function(){
3632 * @class Roo.bootstrap.Modal
3633 * @extends Roo.bootstrap.Component
3634 * Bootstrap Modal class
3635 * @cfg {String} title Title of dialog
3636 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3637 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3638 * @cfg {Boolean} specificTitle default false
3639 * @cfg {Array} buttons Array of buttons or standard button set..
3640 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3641 * @cfg {Boolean} animate default true
3642 * @cfg {Boolean} allow_close default true
3643 * @cfg {Boolean} fitwindow default false
3644 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3645 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3646 * @cfg {String} size (sm|lg) default empty
3647 * @cfg {Number} max_width set the max width of modal
3651 * Create a new Modal Dialog
3652 * @param {Object} config The config object
3655 Roo.bootstrap.Modal = function(config){
3656 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3661 * The raw btnclick event for the button
3662 * @param {Roo.EventObject} e
3667 * Fire when dialog resize
3668 * @param {Roo.bootstrap.Modal} this
3669 * @param {Roo.EventObject} e
3673 this.buttons = this.buttons || [];
3676 this.tmpl = Roo.factory(this.tmpl);
3681 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3683 title : 'test dialog',
3693 specificTitle: false,
3695 buttonPosition: 'right',
3718 onRender : function(ct, position)
3720 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3723 var cfg = Roo.apply({}, this.getAutoCreate());
3726 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3728 //if (!cfg.name.length) {
3732 cfg.cls += ' ' + this.cls;
3735 cfg.style = this.style;
3737 this.el = Roo.get(document.body).createChild(cfg, position);
3739 //var type = this.el.dom.type;
3742 if(this.tabIndex !== undefined){
3743 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3746 this.dialogEl = this.el.select('.modal-dialog',true).first();
3747 this.bodyEl = this.el.select('.modal-body',true).first();
3748 this.closeEl = this.el.select('.modal-header .close', true).first();
3749 this.headerEl = this.el.select('.modal-header',true).first();
3750 this.titleEl = this.el.select('.modal-title',true).first();
3751 this.footerEl = this.el.select('.modal-footer',true).first();
3753 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3755 //this.el.addClass("x-dlg-modal");
3757 if (this.buttons.length) {
3758 Roo.each(this.buttons, function(bb) {
3759 var b = Roo.apply({}, bb);
3760 b.xns = b.xns || Roo.bootstrap;
3761 b.xtype = b.xtype || 'Button';
3762 if (typeof(b.listeners) == 'undefined') {
3763 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3766 var btn = Roo.factory(b);
3768 btn.render(this.getButtonContainer());
3772 // render the children.
3775 if(typeof(this.items) != 'undefined'){
3776 var items = this.items;
3779 for(var i =0;i < items.length;i++) {
3780 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3784 this.items = nitems;
3786 // where are these used - they used to be body/close/footer
3790 //this.el.addClass([this.fieldClass, this.cls]);
3794 getAutoCreate : function()
3796 // we will default to modal-body-overflow - might need to remove or make optional later.
3798 cls : 'modal-body enable-modal-body-overflow ',
3799 html : this.html || ''
3804 cls : 'modal-title',
3808 if(this.specificTitle){
3814 if (this.allow_close && Roo.bootstrap.version == 3) {
3824 if (this.allow_close && Roo.bootstrap.version == 4) {
3834 if(this.size.length){
3835 size = 'modal-' + this.size;
3838 var footer = Roo.bootstrap.version == 3 ?
3840 cls : 'modal-footer',
3844 cls: 'btn-' + this.buttonPosition
3849 { // BS4 uses mr-auto on left buttons....
3850 cls : 'modal-footer'
3861 cls: "modal-dialog " + size,
3864 cls : "modal-content",
3867 cls : 'modal-header',
3882 modal.cls += ' fade';
3888 getChildContainer : function() {
3893 getButtonContainer : function() {
3895 return Roo.bootstrap.version == 4 ?
3896 this.el.select('.modal-footer',true).first()
3897 : this.el.select('.modal-footer div',true).first();
3900 initEvents : function()
3902 if (this.allow_close) {
3903 this.closeEl.on('click', this.hide, this);
3905 Roo.EventManager.onWindowResize(this.resize, this, true);
3913 this.maskEl.setSize(
3914 Roo.lib.Dom.getViewWidth(true),
3915 Roo.lib.Dom.getViewHeight(true)
3918 if (this.fitwindow) {
3922 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3923 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3928 if(this.max_width !== 0) {
3930 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3933 this.setSize(w, this.height);
3937 if(this.max_height) {
3938 this.setSize(w,Math.min(
3940 Roo.lib.Dom.getViewportHeight(true) - 60
3946 if(!this.fit_content) {
3947 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3951 this.setSize(w, Math.min(
3953 this.headerEl.getHeight() +
3954 this.footerEl.getHeight() +
3955 this.getChildHeight(this.bodyEl.dom.childNodes),
3956 Roo.lib.Dom.getViewportHeight(true) - 60)
3962 setSize : function(w,h)
3973 if (!this.rendered) {
3977 //this.el.setStyle('display', 'block');
3978 this.el.removeClass('hideing');
3979 this.el.dom.style.display='block';
3981 Roo.get(document.body).addClass('modal-open');
3983 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3986 this.el.addClass('show');
3987 this.el.addClass('in');
3990 this.el.addClass('show');
3991 this.el.addClass('in');
3994 // not sure how we can show data in here..
3996 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3999 Roo.get(document.body).addClass("x-body-masked");
4001 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4002 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4003 this.maskEl.dom.style.display = 'block';
4004 this.maskEl.addClass('show');
4009 this.fireEvent('show', this);
4011 // set zindex here - otherwise it appears to be ignored...
4012 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4015 this.items.forEach( function(e) {
4016 e.layout ? e.layout() : false;
4024 if(this.fireEvent("beforehide", this) !== false){
4026 this.maskEl.removeClass('show');
4028 this.maskEl.dom.style.display = '';
4029 Roo.get(document.body).removeClass("x-body-masked");
4030 this.el.removeClass('in');
4031 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4033 if(this.animate){ // why
4034 this.el.addClass('hideing');
4035 this.el.removeClass('show');
4037 if (!this.el.hasClass('hideing')) {
4038 return; // it's been shown again...
4041 this.el.dom.style.display='';
4043 Roo.get(document.body).removeClass('modal-open');
4044 this.el.removeClass('hideing');
4048 this.el.removeClass('show');
4049 this.el.dom.style.display='';
4050 Roo.get(document.body).removeClass('modal-open');
4053 this.fireEvent('hide', this);
4056 isVisible : function()
4059 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4063 addButton : function(str, cb)
4067 var b = Roo.apply({}, { html : str } );
4068 b.xns = b.xns || Roo.bootstrap;
4069 b.xtype = b.xtype || 'Button';
4070 if (typeof(b.listeners) == 'undefined') {
4071 b.listeners = { click : cb.createDelegate(this) };
4074 var btn = Roo.factory(b);
4076 btn.render(this.getButtonContainer());
4082 setDefaultButton : function(btn)
4084 //this.el.select('.modal-footer').()
4087 resizeTo: function(w,h)
4089 this.dialogEl.setWidth(w);
4091 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4093 this.bodyEl.setHeight(h - diff);
4095 this.fireEvent('resize', this);
4098 setContentSize : function(w, h)
4102 onButtonClick: function(btn,e)
4105 this.fireEvent('btnclick', btn.name, e);
4108 * Set the title of the Dialog
4109 * @param {String} str new Title
4111 setTitle: function(str) {
4112 this.titleEl.dom.innerHTML = str;
4115 * Set the body of the Dialog
4116 * @param {String} str new Title
4118 setBody: function(str) {
4119 this.bodyEl.dom.innerHTML = str;
4122 * Set the body of the Dialog using the template
4123 * @param {Obj} data - apply this data to the template and replace the body contents.
4125 applyBody: function(obj)
4128 Roo.log("Error - using apply Body without a template");
4131 this.tmpl.overwrite(this.bodyEl, obj);
4134 getChildHeight : function(child_nodes)
4138 child_nodes.length == 0
4143 var child_height = 0;
4145 for(var i = 0; i < child_nodes.length; i++) {
4148 * for modal with tabs...
4149 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4151 var layout_childs = child_nodes[i].childNodes;
4153 for(var j = 0; j < layout_childs.length; j++) {
4155 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4157 var layout_body_childs = layout_childs[j].childNodes;
4159 for(var k = 0; k < layout_body_childs.length; k++) {
4161 if(layout_body_childs[k].classList.contains('navbar')) {
4162 child_height += layout_body_childs[k].offsetHeight;
4166 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4168 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4170 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4172 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4173 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4188 child_height += child_nodes[i].offsetHeight;
4189 // Roo.log(child_nodes[i].offsetHeight);
4192 return child_height;
4198 Roo.apply(Roo.bootstrap.Modal, {
4200 * Button config that displays a single OK button
4209 * Button config that displays Yes and No buttons
4225 * Button config that displays OK and Cancel buttons
4240 * Button config that displays Yes, No and Cancel buttons
4264 * messagebox - can be used as a replace
4268 * @class Roo.MessageBox
4269 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4273 Roo.Msg.alert('Status', 'Changes saved successfully.');
4275 // Prompt for user data:
4276 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4278 // process text value...
4282 // Show a dialog using config options:
4284 title:'Save Changes?',
4285 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4286 buttons: Roo.Msg.YESNOCANCEL,
4293 Roo.bootstrap.MessageBox = function(){
4294 var dlg, opt, mask, waitTimer;
4295 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4296 var buttons, activeTextEl, bwidth;
4300 var handleButton = function(button){
4302 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4306 var handleHide = function(){
4308 dlg.el.removeClass(opt.cls);
4311 // Roo.TaskMgr.stop(waitTimer);
4312 // waitTimer = null;
4317 var updateButtons = function(b){
4320 buttons["ok"].hide();
4321 buttons["cancel"].hide();
4322 buttons["yes"].hide();
4323 buttons["no"].hide();
4324 dlg.footerEl.hide();
4328 dlg.footerEl.show();
4329 for(var k in buttons){
4330 if(typeof buttons[k] != "function"){
4333 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4334 width += buttons[k].el.getWidth()+15;
4344 var handleEsc = function(d, k, e){
4345 if(opt && opt.closable !== false){
4355 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4356 * @return {Roo.BasicDialog} The BasicDialog element
4358 getDialog : function(){
4360 dlg = new Roo.bootstrap.Modal( {
4363 //constraintoviewport:false,
4365 //collapsible : false,
4370 //buttonAlign:"center",
4371 closeClick : function(){
4372 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4375 handleButton("cancel");
4380 dlg.on("hide", handleHide);
4382 //dlg.addKeyListener(27, handleEsc);
4384 this.buttons = buttons;
4385 var bt = this.buttonText;
4386 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4387 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4388 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4389 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4391 bodyEl = dlg.bodyEl.createChild({
4393 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4394 '<textarea class="roo-mb-textarea"></textarea>' +
4395 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4397 msgEl = bodyEl.dom.firstChild;
4398 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4399 textboxEl.enableDisplayMode();
4400 textboxEl.addKeyListener([10,13], function(){
4401 if(dlg.isVisible() && opt && opt.buttons){
4404 }else if(opt.buttons.yes){
4405 handleButton("yes");
4409 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4410 textareaEl.enableDisplayMode();
4411 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4412 progressEl.enableDisplayMode();
4414 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4415 var pf = progressEl.dom.firstChild;
4417 pp = Roo.get(pf.firstChild);
4418 pp.setHeight(pf.offsetHeight);
4426 * Updates the message box body text
4427 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4428 * the XHTML-compliant non-breaking space character '&#160;')
4429 * @return {Roo.MessageBox} This message box
4431 updateText : function(text)
4433 if(!dlg.isVisible() && !opt.width){
4434 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4435 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4437 msgEl.innerHTML = text || ' ';
4439 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4440 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4442 Math.min(opt.width || cw , this.maxWidth),
4443 Math.max(opt.minWidth || this.minWidth, bwidth)
4446 activeTextEl.setWidth(w);
4448 if(dlg.isVisible()){
4449 dlg.fixedcenter = false;
4451 // to big, make it scroll. = But as usual stupid IE does not support
4454 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4455 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4456 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4458 bodyEl.dom.style.height = '';
4459 bodyEl.dom.style.overflowY = '';
4462 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4464 bodyEl.dom.style.overflowX = '';
4467 dlg.setContentSize(w, bodyEl.getHeight());
4468 if(dlg.isVisible()){
4469 dlg.fixedcenter = true;
4475 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4476 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4477 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4478 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4479 * @return {Roo.MessageBox} This message box
4481 updateProgress : function(value, text){
4483 this.updateText(text);
4486 if (pp) { // weird bug on my firefox - for some reason this is not defined
4487 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4488 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4494 * Returns true if the message box is currently displayed
4495 * @return {Boolean} True if the message box is visible, else false
4497 isVisible : function(){
4498 return dlg && dlg.isVisible();
4502 * Hides the message box if it is displayed
4505 if(this.isVisible()){
4511 * Displays a new message box, or reinitializes an existing message box, based on the config options
4512 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4513 * The following config object properties are supported:
4515 Property Type Description
4516 ---------- --------------- ------------------------------------------------------------------------------------
4517 animEl String/Element An id or Element from which the message box should animate as it opens and
4518 closes (defaults to undefined)
4519 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4520 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4521 closable Boolean False to hide the top-right close button (defaults to true). Note that
4522 progress and wait dialogs will ignore this property and always hide the
4523 close button as they can only be closed programmatically.
4524 cls String A custom CSS class to apply to the message box element
4525 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4526 displayed (defaults to 75)
4527 fn Function A callback function to execute after closing the dialog. The arguments to the
4528 function will be btn (the name of the button that was clicked, if applicable,
4529 e.g. "ok"), and text (the value of the active text field, if applicable).
4530 Progress and wait dialogs will ignore this option since they do not respond to
4531 user actions and can only be closed programmatically, so any required function
4532 should be called by the same code after it closes the dialog.
4533 icon String A CSS class that provides a background image to be used as an icon for
4534 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4535 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4536 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4537 modal Boolean False to allow user interaction with the page while the message box is
4538 displayed (defaults to true)
4539 msg String A string that will replace the existing message box body text (defaults
4540 to the XHTML-compliant non-breaking space character ' ')
4541 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4542 progress Boolean True to display a progress bar (defaults to false)
4543 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4544 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4545 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4546 title String The title text
4547 value String The string value to set into the active textbox element if displayed
4548 wait Boolean True to display a progress bar (defaults to false)
4549 width Number The width of the dialog in pixels
4556 msg: 'Please enter your address:',
4558 buttons: Roo.MessageBox.OKCANCEL,
4561 animEl: 'addAddressBtn'
4564 * @param {Object} config Configuration options
4565 * @return {Roo.MessageBox} This message box
4567 show : function(options)
4570 // this causes nightmares if you show one dialog after another
4571 // especially on callbacks..
4573 if(this.isVisible()){
4576 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4577 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4578 Roo.log("New Dialog Message:" + options.msg )
4579 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4580 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4583 var d = this.getDialog();
4585 d.setTitle(opt.title || " ");
4586 d.closeEl.setDisplayed(opt.closable !== false);
4587 activeTextEl = textboxEl;
4588 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4593 textareaEl.setHeight(typeof opt.multiline == "number" ?
4594 opt.multiline : this.defaultTextHeight);
4595 activeTextEl = textareaEl;
4604 progressEl.setDisplayed(opt.progress === true);
4606 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4608 this.updateProgress(0);
4609 activeTextEl.dom.value = opt.value || "";
4611 dlg.setDefaultButton(activeTextEl);
4613 var bs = opt.buttons;
4617 }else if(bs && bs.yes){
4618 db = buttons["yes"];
4620 dlg.setDefaultButton(db);
4622 bwidth = updateButtons(opt.buttons);
4623 this.updateText(opt.msg);
4625 d.el.addClass(opt.cls);
4627 d.proxyDrag = opt.proxyDrag === true;
4628 d.modal = opt.modal !== false;
4629 d.mask = opt.modal !== false ? mask : false;
4631 // force it to the end of the z-index stack so it gets a cursor in FF
4632 document.body.appendChild(dlg.el.dom);
4633 d.animateTarget = null;
4634 d.show(options.animEl);
4640 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4641 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4642 * and closing the message box when the process is complete.
4643 * @param {String} title The title bar text
4644 * @param {String} msg The message box body text
4645 * @return {Roo.MessageBox} This message box
4647 progress : function(title, msg){
4654 minWidth: this.minProgressWidth,
4661 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4662 * If a callback function is passed it will be called after the user clicks the button, and the
4663 * id of the button that was clicked will be passed as the only parameter to the callback
4664 * (could also be the top-right close button).
4665 * @param {String} title The title bar text
4666 * @param {String} msg The message box body text
4667 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4668 * @param {Object} scope (optional) The scope of the callback function
4669 * @return {Roo.MessageBox} This message box
4671 alert : function(title, msg, fn, scope)
4686 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4687 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4688 * You are responsible for closing the message box when the process is complete.
4689 * @param {String} msg The message box body text
4690 * @param {String} title (optional) The title bar text
4691 * @return {Roo.MessageBox} This message box
4693 wait : function(msg, title){
4704 waitTimer = Roo.TaskMgr.start({
4706 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4714 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4715 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4716 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4717 * @param {String} title The title bar text
4718 * @param {String} msg The message box body text
4719 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4720 * @param {Object} scope (optional) The scope of the callback function
4721 * @return {Roo.MessageBox} This message box
4723 confirm : function(title, msg, fn, scope){
4727 buttons: this.YESNO,
4736 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4737 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4738 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4739 * (could also be the top-right close button) and the text that was entered will be passed as the two
4740 * parameters to the callback.
4741 * @param {String} title The title bar text
4742 * @param {String} msg The message box body text
4743 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4744 * @param {Object} scope (optional) The scope of the callback function
4745 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4746 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4747 * @return {Roo.MessageBox} This message box
4749 prompt : function(title, msg, fn, scope, multiline){
4753 buttons: this.OKCANCEL,
4758 multiline: multiline,
4765 * Button config that displays a single OK button
4770 * Button config that displays Yes and No buttons
4773 YESNO : {yes:true, no:true},
4775 * Button config that displays OK and Cancel buttons
4778 OKCANCEL : {ok:true, cancel:true},
4780 * Button config that displays Yes, No and Cancel buttons
4783 YESNOCANCEL : {yes:true, no:true, cancel:true},
4786 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4789 defaultTextHeight : 75,
4791 * The maximum width in pixels of the message box (defaults to 600)
4796 * The minimum width in pixels of the message box (defaults to 100)
4801 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4802 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4805 minProgressWidth : 250,
4807 * An object containing the default button text strings that can be overriden for localized language support.
4808 * Supported properties are: ok, cancel, yes and no.
4809 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4822 * Shorthand for {@link Roo.MessageBox}
4824 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4825 Roo.Msg = Roo.Msg || Roo.MessageBox;
4834 * @class Roo.bootstrap.Navbar
4835 * @extends Roo.bootstrap.Component
4836 * Bootstrap Navbar class
4839 * Create a new Navbar
4840 * @param {Object} config The config object
4844 Roo.bootstrap.Navbar = function(config){
4845 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4849 * @event beforetoggle
4850 * Fire before toggle the menu
4851 * @param {Roo.EventObject} e
4853 "beforetoggle" : true
4857 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4866 getAutoCreate : function(){
4869 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4873 initEvents :function ()
4875 //Roo.log(this.el.select('.navbar-toggle',true));
4876 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4883 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4885 var size = this.el.getSize();
4886 this.maskEl.setSize(size.width, size.height);
4887 this.maskEl.enableDisplayMode("block");
4896 getChildContainer : function()
4898 if (this.el && this.el.select('.collapse').getCount()) {
4899 return this.el.select('.collapse',true).first();
4914 onToggle : function()
4917 if(this.fireEvent('beforetoggle', this) === false){
4920 var ce = this.el.select('.navbar-collapse',true).first();
4922 if (!ce.hasClass('show')) {
4932 * Expand the navbar pulldown
4934 expand : function ()
4937 var ce = this.el.select('.navbar-collapse',true).first();
4938 if (ce.hasClass('collapsing')) {
4941 ce.dom.style.height = '';
4943 ce.addClass('in'); // old...
4944 ce.removeClass('collapse');
4945 ce.addClass('show');
4946 var h = ce.getHeight();
4948 ce.removeClass('show');
4949 // at this point we should be able to see it..
4950 ce.addClass('collapsing');
4952 ce.setHeight(0); // resize it ...
4953 ce.on('transitionend', function() {
4954 //Roo.log('done transition');
4955 ce.removeClass('collapsing');
4956 ce.addClass('show');
4957 ce.removeClass('collapse');
4959 ce.dom.style.height = '';
4960 }, this, { single: true} );
4962 ce.dom.scrollTop = 0;
4965 * Collapse the navbar pulldown
4967 collapse : function()
4969 var ce = this.el.select('.navbar-collapse',true).first();
4971 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4972 // it's collapsed or collapsing..
4975 ce.removeClass('in'); // old...
4976 ce.setHeight(ce.getHeight());
4977 ce.removeClass('show');
4978 ce.addClass('collapsing');
4980 ce.on('transitionend', function() {
4981 ce.dom.style.height = '';
4982 ce.removeClass('collapsing');
4983 ce.addClass('collapse');
4984 }, this, { single: true} );
5004 * @class Roo.bootstrap.NavSimplebar
5005 * @extends Roo.bootstrap.Navbar
5006 * Bootstrap Sidebar class
5008 * @cfg {Boolean} inverse is inverted color
5010 * @cfg {String} type (nav | pills | tabs)
5011 * @cfg {Boolean} arrangement stacked | justified
5012 * @cfg {String} align (left | right) alignment
5014 * @cfg {Boolean} main (true|false) main nav bar? default false
5015 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5017 * @cfg {String} tag (header|footer|nav|div) default is nav
5019 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5023 * Create a new Sidebar
5024 * @param {Object} config The config object
5028 Roo.bootstrap.NavSimplebar = function(config){
5029 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5032 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5048 getAutoCreate : function(){
5052 tag : this.tag || 'div',
5053 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5055 if (['light','white'].indexOf(this.weight) > -1) {
5056 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5058 cfg.cls += ' bg-' + this.weight;
5061 cfg.cls += ' navbar-inverse';
5065 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5067 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5076 cls: 'nav nav-' + this.xtype,
5082 this.type = this.type || 'nav';
5083 if (['tabs','pills'].indexOf(this.type) != -1) {
5084 cfg.cn[0].cls += ' nav-' + this.type
5088 if (this.type!=='nav') {
5089 Roo.log('nav type must be nav/tabs/pills')
5091 cfg.cn[0].cls += ' navbar-nav'
5097 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5098 cfg.cn[0].cls += ' nav-' + this.arrangement;
5102 if (this.align === 'right') {
5103 cfg.cn[0].cls += ' navbar-right';
5128 * navbar-expand-md fixed-top
5132 * @class Roo.bootstrap.NavHeaderbar
5133 * @extends Roo.bootstrap.NavSimplebar
5134 * Bootstrap Sidebar class
5136 * @cfg {String} brand what is brand
5137 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5138 * @cfg {String} brand_href href of the brand
5139 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5140 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5141 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5142 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5145 * Create a new Sidebar
5146 * @param {Object} config The config object
5150 Roo.bootstrap.NavHeaderbar = function(config){
5151 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5155 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5162 desktopCenter : false,
5165 getAutoCreate : function(){
5168 tag: this.nav || 'nav',
5169 cls: 'navbar navbar-expand-md',
5175 if (this.desktopCenter) {
5176 cn.push({cls : 'container', cn : []});
5184 cls: 'navbar-toggle navbar-toggler',
5185 'data-toggle': 'collapse',
5190 html: 'Toggle navigation'
5194 cls: 'icon-bar navbar-toggler-icon'
5207 cn.push( Roo.bootstrap.version == 4 ? btn : {
5209 cls: 'navbar-header',
5218 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5222 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5224 if (['light','white'].indexOf(this.weight) > -1) {
5225 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5227 cfg.cls += ' bg-' + this.weight;
5230 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5231 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5233 // tag can override this..
5235 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5238 if (this.brand !== '') {
5239 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5240 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5242 href: this.brand_href ? this.brand_href : '#',
5243 cls: 'navbar-brand',
5251 cfg.cls += ' main-nav';
5259 getHeaderChildContainer : function()
5261 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5262 return this.el.select('.navbar-header',true).first();
5265 return this.getChildContainer();
5268 getChildContainer : function()
5271 return this.el.select('.roo-navbar-collapse',true).first();
5276 initEvents : function()
5278 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5280 if (this.autohide) {
5285 Roo.get(document).on('scroll',function(e) {
5286 var ns = Roo.get(document).getScroll().top;
5287 var os = prevScroll;
5291 ft.removeClass('slideDown');
5292 ft.addClass('slideUp');
5295 ft.removeClass('slideUp');
5296 ft.addClass('slideDown');
5317 * @class Roo.bootstrap.NavSidebar
5318 * @extends Roo.bootstrap.Navbar
5319 * Bootstrap Sidebar class
5322 * Create a new Sidebar
5323 * @param {Object} config The config object
5327 Roo.bootstrap.NavSidebar = function(config){
5328 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5331 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5333 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5335 getAutoCreate : function(){
5340 cls: 'sidebar sidebar-nav'
5362 * @class Roo.bootstrap.NavGroup
5363 * @extends Roo.bootstrap.Component
5364 * Bootstrap NavGroup class
5365 * @cfg {String} align (left|right)
5366 * @cfg {Boolean} inverse
5367 * @cfg {String} type (nav|pills|tab) default nav
5368 * @cfg {String} navId - reference Id for navbar.
5372 * Create a new nav group
5373 * @param {Object} config The config object
5376 Roo.bootstrap.NavGroup = function(config){
5377 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5380 Roo.bootstrap.NavGroup.register(this);
5384 * Fires when the active item changes
5385 * @param {Roo.bootstrap.NavGroup} this
5386 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5387 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5394 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5405 getAutoCreate : function()
5407 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5413 if (Roo.bootstrap.version == 4) {
5414 if (['tabs','pills'].indexOf(this.type) != -1) {
5415 cfg.cls += ' nav-' + this.type;
5417 // trying to remove so header bar can right align top?
5418 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5419 // do not use on header bar...
5420 cfg.cls += ' navbar-nav';
5425 if (['tabs','pills'].indexOf(this.type) != -1) {
5426 cfg.cls += ' nav-' + this.type
5428 if (this.type !== 'nav') {
5429 Roo.log('nav type must be nav/tabs/pills')
5431 cfg.cls += ' navbar-nav'
5435 if (this.parent() && this.parent().sidebar) {
5438 cls: 'dashboard-menu sidebar-menu'
5444 if (this.form === true) {
5447 cls: 'navbar-form form-inline'
5449 //nav navbar-right ml-md-auto
5450 if (this.align === 'right') {
5451 cfg.cls += ' navbar-right ml-md-auto';
5453 cfg.cls += ' navbar-left';
5457 if (this.align === 'right') {
5458 cfg.cls += ' navbar-right ml-md-auto';
5460 cfg.cls += ' mr-auto';
5464 cfg.cls += ' navbar-inverse';
5472 * sets the active Navigation item
5473 * @param {Roo.bootstrap.NavItem} the new current navitem
5475 setActiveItem : function(item)
5478 Roo.each(this.navItems, function(v){
5483 v.setActive(false, true);
5490 item.setActive(true, true);
5491 this.fireEvent('changed', this, item, prev);
5496 * gets the active Navigation item
5497 * @return {Roo.bootstrap.NavItem} the current navitem
5499 getActive : function()
5503 Roo.each(this.navItems, function(v){
5514 indexOfNav : function()
5518 Roo.each(this.navItems, function(v,i){
5529 * adds a Navigation item
5530 * @param {Roo.bootstrap.NavItem} the navitem to add
5532 addItem : function(cfg)
5534 if (this.form && Roo.bootstrap.version == 4) {
5537 var cn = new Roo.bootstrap.NavItem(cfg);
5539 cn.parentId = this.id;
5540 cn.onRender(this.el, null);
5544 * register a Navigation item
5545 * @param {Roo.bootstrap.NavItem} the navitem to add
5547 register : function(item)
5549 this.navItems.push( item);
5550 item.navId = this.navId;
5555 * clear all the Navigation item
5558 clearAll : function()
5561 this.el.dom.innerHTML = '';
5564 getNavItem: function(tabId)
5567 Roo.each(this.navItems, function(e) {
5568 if (e.tabId == tabId) {
5578 setActiveNext : function()
5580 var i = this.indexOfNav(this.getActive());
5581 if (i > this.navItems.length) {
5584 this.setActiveItem(this.navItems[i+1]);
5586 setActivePrev : function()
5588 var i = this.indexOfNav(this.getActive());
5592 this.setActiveItem(this.navItems[i-1]);
5594 clearWasActive : function(except) {
5595 Roo.each(this.navItems, function(e) {
5596 if (e.tabId != except.tabId && e.was_active) {
5597 e.was_active = false;
5604 getWasActive : function ()
5607 Roo.each(this.navItems, function(e) {
5622 Roo.apply(Roo.bootstrap.NavGroup, {
5626 * register a Navigation Group
5627 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5629 register : function(navgrp)
5631 this.groups[navgrp.navId] = navgrp;
5635 * fetch a Navigation Group based on the navigation ID
5636 * @param {string} the navgroup to add
5637 * @returns {Roo.bootstrap.NavGroup} the navgroup
5639 get: function(navId) {
5640 if (typeof(this.groups[navId]) == 'undefined') {
5642 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5644 return this.groups[navId] ;
5659 * @class Roo.bootstrap.NavItem
5660 * @extends Roo.bootstrap.Component
5661 * Bootstrap Navbar.NavItem class
5662 * @cfg {String} href link to
5663 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5665 * @cfg {String} html content of button
5666 * @cfg {String} badge text inside badge
5667 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5668 * @cfg {String} glyphicon DEPRICATED - use fa
5669 * @cfg {String} icon DEPRICATED - use fa
5670 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5671 * @cfg {Boolean} active Is item active
5672 * @cfg {Boolean} disabled Is item disabled
5674 * @cfg {Boolean} preventDefault (true | false) default false
5675 * @cfg {String} tabId the tab that this item activates.
5676 * @cfg {String} tagtype (a|span) render as a href or span?
5677 * @cfg {Boolean} animateRef (true|false) link to element default false
5680 * Create a new Navbar Item
5681 * @param {Object} config The config object
5683 Roo.bootstrap.NavItem = function(config){
5684 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5689 * The raw click event for the entire grid.
5690 * @param {Roo.EventObject} e
5695 * Fires when the active item active state changes
5696 * @param {Roo.bootstrap.NavItem} this
5697 * @param {boolean} state the new state
5703 * Fires when scroll to element
5704 * @param {Roo.bootstrap.NavItem} this
5705 * @param {Object} options
5706 * @param {Roo.EventObject} e
5714 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5723 preventDefault : false,
5731 button_outline : false,
5735 getAutoCreate : function(){
5743 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5745 if (this.disabled) {
5746 cfg.cls += ' disabled';
5750 if (this.button_weight.length) {
5751 cfg.tag = this.href ? 'a' : 'button';
5752 cfg.html = this.html || '';
5753 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5755 cfg.href = this.href;
5758 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5761 // menu .. should add dropdown-menu class - so no need for carat..
5763 if (this.badge !== '') {
5765 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5770 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5774 href : this.href || "#",
5775 html: this.html || ''
5778 if (this.tagtype == 'a') {
5779 cfg.cn[0].cls = 'nav-link';
5782 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5785 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5787 if(this.glyphicon) {
5788 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5793 cfg.cn[0].html += " <span class='caret'></span>";
5797 if (this.badge !== '') {
5799 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5807 onRender : function(ct, position)
5809 // Roo.log("Call onRender: " + this.xtype);
5810 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5814 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5815 this.navLink = this.el.select('.nav-link',true).first();
5820 initEvents: function()
5822 if (typeof (this.menu) != 'undefined') {
5823 this.menu.parentType = this.xtype;
5824 this.menu.triggerEl = this.el;
5825 this.menu = this.addxtype(Roo.apply({}, this.menu));
5828 this.el.select('a',true).on('click', this.onClick, this);
5830 if(this.tagtype == 'span'){
5831 this.el.select('span',true).on('click', this.onClick, this);
5834 // at this point parent should be available..
5835 this.parent().register(this);
5838 onClick : function(e)
5840 if (e.getTarget('.dropdown-menu-item')) {
5841 // did you click on a menu itemm.... - then don't trigger onclick..
5846 this.preventDefault ||
5849 Roo.log("NavItem - prevent Default?");
5853 if (this.disabled) {
5857 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5858 if (tg && tg.transition) {
5859 Roo.log("waiting for the transitionend");
5865 //Roo.log("fire event clicked");
5866 if(this.fireEvent('click', this, e) === false){
5870 if(this.tagtype == 'span'){
5874 //Roo.log(this.href);
5875 var ael = this.el.select('a',true).first();
5878 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5879 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5880 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5881 return; // ignore... - it's a 'hash' to another page.
5883 Roo.log("NavItem - prevent Default?");
5885 this.scrollToElement(e);
5889 var p = this.parent();
5891 if (['tabs','pills'].indexOf(p.type)!==-1) {
5892 if (typeof(p.setActiveItem) !== 'undefined') {
5893 p.setActiveItem(this);
5897 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5898 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5899 // remove the collapsed menu expand...
5900 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5904 isActive: function () {
5907 setActive : function(state, fire, is_was_active)
5909 if (this.active && !state && this.navId) {
5910 this.was_active = true;
5911 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5913 nv.clearWasActive(this);
5917 this.active = state;
5920 this.el.removeClass('active');
5921 this.navLink ? this.navLink.removeClass('active') : false;
5922 } else if (!this.el.hasClass('active')) {
5924 this.el.addClass('active');
5925 if (Roo.bootstrap.version == 4 && this.navLink ) {
5926 this.navLink.addClass('active');
5931 this.fireEvent('changed', this, state);
5934 // show a panel if it's registered and related..
5936 if (!this.navId || !this.tabId || !state || is_was_active) {
5940 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5944 var pan = tg.getPanelByName(this.tabId);
5948 // if we can not flip to new panel - go back to old nav highlight..
5949 if (false == tg.showPanel(pan)) {
5950 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5952 var onav = nv.getWasActive();
5954 onav.setActive(true, false, true);
5963 // this should not be here...
5964 setDisabled : function(state)
5966 this.disabled = state;
5968 this.el.removeClass('disabled');
5969 } else if (!this.el.hasClass('disabled')) {
5970 this.el.addClass('disabled');
5976 * Fetch the element to display the tooltip on.
5977 * @return {Roo.Element} defaults to this.el
5979 tooltipEl : function()
5981 return this.el.select('' + this.tagtype + '', true).first();
5984 scrollToElement : function(e)
5986 var c = document.body;
5989 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5991 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5992 c = document.documentElement;
5995 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6001 var o = target.calcOffsetsTo(c);
6008 this.fireEvent('scrollto', this, options, e);
6010 Roo.get(c).scrollTo('top', options.value, true);
6023 * <span> icon </span>
6024 * <span> text </span>
6025 * <span>badge </span>
6029 * @class Roo.bootstrap.NavSidebarItem
6030 * @extends Roo.bootstrap.NavItem
6031 * Bootstrap Navbar.NavSidebarItem class
6032 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6033 * {Boolean} open is the menu open
6034 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6035 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6036 * {String} buttonSize (sm|md|lg)the extra classes for the button
6037 * {Boolean} showArrow show arrow next to the text (default true)
6039 * Create a new Navbar Button
6040 * @param {Object} config The config object
6042 Roo.bootstrap.NavSidebarItem = function(config){
6043 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6048 * The raw click event for the entire grid.
6049 * @param {Roo.EventObject} e
6054 * Fires when the active item active state changes
6055 * @param {Roo.bootstrap.NavSidebarItem} this
6056 * @param {boolean} state the new state
6064 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6066 badgeWeight : 'default',
6072 buttonWeight : 'default',
6078 getAutoCreate : function(){
6083 href : this.href || '#',
6089 if(this.buttonView){
6092 href : this.href || '#',
6093 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6106 cfg.cls += ' active';
6109 if (this.disabled) {
6110 cfg.cls += ' disabled';
6113 cfg.cls += ' open x-open';
6116 if (this.glyphicon || this.icon) {
6117 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6118 a.cn.push({ tag : 'i', cls : c }) ;
6121 if(!this.buttonView){
6124 html : this.html || ''
6131 if (this.badge !== '') {
6132 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6138 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6141 a.cls += ' dropdown-toggle treeview' ;
6147 initEvents : function()
6149 if (typeof (this.menu) != 'undefined') {
6150 this.menu.parentType = this.xtype;
6151 this.menu.triggerEl = this.el;
6152 this.menu = this.addxtype(Roo.apply({}, this.menu));
6155 this.el.on('click', this.onClick, this);
6157 if(this.badge !== ''){
6158 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6163 onClick : function(e)
6170 if(this.preventDefault){
6174 this.fireEvent('click', this, e);
6177 disable : function()
6179 this.setDisabled(true);
6184 this.setDisabled(false);
6187 setDisabled : function(state)
6189 if(this.disabled == state){
6193 this.disabled = state;
6196 this.el.addClass('disabled');
6200 this.el.removeClass('disabled');
6205 setActive : function(state)
6207 if(this.active == state){
6211 this.active = state;
6214 this.el.addClass('active');
6218 this.el.removeClass('active');
6223 isActive: function ()
6228 setBadge : function(str)
6234 this.badgeEl.dom.innerHTML = str;
6251 * @class Roo.bootstrap.Row
6252 * @extends Roo.bootstrap.Component
6253 * Bootstrap Row class (contains columns...)
6257 * @param {Object} config The config object
6260 Roo.bootstrap.Row = function(config){
6261 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6264 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6266 getAutoCreate : function(){
6285 * @class Roo.bootstrap.Pagination
6286 * @extends Roo.bootstrap.Component
6287 * Bootstrap Pagination class
6288 * @cfg {String} size xs | sm | md | lg
6289 * @cfg {Boolean} inverse false | true
6292 * Create a new Pagination
6293 * @param {Object} config The config object
6296 Roo.bootstrap.Pagination = function(config){
6297 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6300 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6306 getAutoCreate : function(){
6312 cfg.cls += ' inverse';
6318 cfg.cls += " " + this.cls;
6336 * @class Roo.bootstrap.PaginationItem
6337 * @extends Roo.bootstrap.Component
6338 * Bootstrap PaginationItem class
6339 * @cfg {String} html text
6340 * @cfg {String} href the link
6341 * @cfg {Boolean} preventDefault (true | false) default true
6342 * @cfg {Boolean} active (true | false) default false
6343 * @cfg {Boolean} disabled default false
6347 * Create a new PaginationItem
6348 * @param {Object} config The config object
6352 Roo.bootstrap.PaginationItem = function(config){
6353 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6358 * The raw click event for the entire grid.
6359 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6369 preventDefault: true,
6374 getAutoCreate : function(){
6380 href : this.href ? this.href : '#',
6381 html : this.html ? this.html : ''
6391 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6395 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6401 initEvents: function() {
6403 this.el.on('click', this.onClick, this);
6406 onClick : function(e)
6408 Roo.log('PaginationItem on click ');
6409 if(this.preventDefault){
6417 this.fireEvent('click', this, e);
6433 * @class Roo.bootstrap.Slider
6434 * @extends Roo.bootstrap.Component
6435 * Bootstrap Slider class
6438 * Create a new Slider
6439 * @param {Object} config The config object
6442 Roo.bootstrap.Slider = function(config){
6443 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6446 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6448 getAutoCreate : function(){
6452 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6456 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6468 * Ext JS Library 1.1.1
6469 * Copyright(c) 2006-2007, Ext JS, LLC.
6471 * Originally Released Under LGPL - original licence link has changed is not relivant.
6474 * <script type="text/javascript">
6479 * @class Roo.grid.ColumnModel
6480 * @extends Roo.util.Observable
6481 * This is the default implementation of a ColumnModel used by the Grid. It defines
6482 * the columns in the grid.
6485 var colModel = new Roo.grid.ColumnModel([
6486 {header: "Ticker", width: 60, sortable: true, locked: true},
6487 {header: "Company Name", width: 150, sortable: true},
6488 {header: "Market Cap.", width: 100, sortable: true},
6489 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6490 {header: "Employees", width: 100, sortable: true, resizable: false}
6495 * The config options listed for this class are options which may appear in each
6496 * individual column definition.
6497 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6499 * @param {Object} config An Array of column config objects. See this class's
6500 * config objects for details.
6502 Roo.grid.ColumnModel = function(config){
6504 * The config passed into the constructor
6506 this.config = config;
6509 // if no id, create one
6510 // if the column does not have a dataIndex mapping,
6511 // map it to the order it is in the config
6512 for(var i = 0, len = config.length; i < len; i++){
6514 if(typeof c.dataIndex == "undefined"){
6517 if(typeof c.renderer == "string"){
6518 c.renderer = Roo.util.Format[c.renderer];
6520 if(typeof c.id == "undefined"){
6523 if(c.editor && c.editor.xtype){
6524 c.editor = Roo.factory(c.editor, Roo.grid);
6526 if(c.editor && c.editor.isFormField){
6527 c.editor = new Roo.grid.GridEditor(c.editor);
6529 this.lookup[c.id] = c;
6533 * The width of columns which have no width specified (defaults to 100)
6536 this.defaultWidth = 100;
6539 * Default sortable of columns which have no sortable specified (defaults to false)
6542 this.defaultSortable = false;
6546 * @event widthchange
6547 * Fires when the width of a column changes.
6548 * @param {ColumnModel} this
6549 * @param {Number} columnIndex The column index
6550 * @param {Number} newWidth The new width
6552 "widthchange": true,
6554 * @event headerchange
6555 * Fires when the text of a header changes.
6556 * @param {ColumnModel} this
6557 * @param {Number} columnIndex The column index
6558 * @param {Number} newText The new header text
6560 "headerchange": true,
6562 * @event hiddenchange
6563 * Fires when a column is hidden or "unhidden".
6564 * @param {ColumnModel} this
6565 * @param {Number} columnIndex The column index
6566 * @param {Boolean} hidden true if hidden, false otherwise
6568 "hiddenchange": true,
6570 * @event columnmoved
6571 * Fires when a column is moved.
6572 * @param {ColumnModel} this
6573 * @param {Number} oldIndex
6574 * @param {Number} newIndex
6576 "columnmoved" : true,
6578 * @event columlockchange
6579 * Fires when a column's locked state is changed
6580 * @param {ColumnModel} this
6581 * @param {Number} colIndex
6582 * @param {Boolean} locked true if locked
6584 "columnlockchange" : true
6586 Roo.grid.ColumnModel.superclass.constructor.call(this);
6588 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6590 * @cfg {String} header The header text to display in the Grid view.
6593 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6594 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6595 * specified, the column's index is used as an index into the Record's data Array.
6598 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6599 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6602 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6603 * Defaults to the value of the {@link #defaultSortable} property.
6604 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6607 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6610 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6613 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6616 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6619 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6620 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6621 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6622 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6625 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6628 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6631 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6634 * @cfg {String} cursor (Optional)
6637 * @cfg {String} tooltip (Optional)
6640 * @cfg {Number} xs (Optional)
6643 * @cfg {Number} sm (Optional)
6646 * @cfg {Number} md (Optional)
6649 * @cfg {Number} lg (Optional)
6652 * Returns the id of the column at the specified index.
6653 * @param {Number} index The column index
6654 * @return {String} the id
6656 getColumnId : function(index){
6657 return this.config[index].id;
6661 * Returns the column for a specified id.
6662 * @param {String} id The column id
6663 * @return {Object} the column
6665 getColumnById : function(id){
6666 return this.lookup[id];
6671 * Returns the column for a specified dataIndex.
6672 * @param {String} dataIndex The column dataIndex
6673 * @return {Object|Boolean} the column or false if not found
6675 getColumnByDataIndex: function(dataIndex){
6676 var index = this.findColumnIndex(dataIndex);
6677 return index > -1 ? this.config[index] : false;
6681 * Returns the index for a specified column id.
6682 * @param {String} id The column id
6683 * @return {Number} the index, or -1 if not found
6685 getIndexById : function(id){
6686 for(var i = 0, len = this.config.length; i < len; i++){
6687 if(this.config[i].id == id){
6695 * Returns the index for a specified column dataIndex.
6696 * @param {String} dataIndex The column dataIndex
6697 * @return {Number} the index, or -1 if not found
6700 findColumnIndex : function(dataIndex){
6701 for(var i = 0, len = this.config.length; i < len; i++){
6702 if(this.config[i].dataIndex == dataIndex){
6710 moveColumn : function(oldIndex, newIndex){
6711 var c = this.config[oldIndex];
6712 this.config.splice(oldIndex, 1);
6713 this.config.splice(newIndex, 0, c);
6714 this.dataMap = null;
6715 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6718 isLocked : function(colIndex){
6719 return this.config[colIndex].locked === true;
6722 setLocked : function(colIndex, value, suppressEvent){
6723 if(this.isLocked(colIndex) == value){
6726 this.config[colIndex].locked = value;
6728 this.fireEvent("columnlockchange", this, colIndex, value);
6732 getTotalLockedWidth : function(){
6734 for(var i = 0; i < this.config.length; i++){
6735 if(this.isLocked(i) && !this.isHidden(i)){
6736 this.totalWidth += this.getColumnWidth(i);
6742 getLockedCount : function(){
6743 for(var i = 0, len = this.config.length; i < len; i++){
6744 if(!this.isLocked(i)){
6749 return this.config.length;
6753 * Returns the number of columns.
6756 getColumnCount : function(visibleOnly){
6757 if(visibleOnly === true){
6759 for(var i = 0, len = this.config.length; i < len; i++){
6760 if(!this.isHidden(i)){
6766 return this.config.length;
6770 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6771 * @param {Function} fn
6772 * @param {Object} scope (optional)
6773 * @return {Array} result
6775 getColumnsBy : function(fn, scope){
6777 for(var i = 0, len = this.config.length; i < len; i++){
6778 var c = this.config[i];
6779 if(fn.call(scope||this, c, i) === true){
6787 * Returns true if the specified column is sortable.
6788 * @param {Number} col The column index
6791 isSortable : function(col){
6792 if(typeof this.config[col].sortable == "undefined"){
6793 return this.defaultSortable;
6795 return this.config[col].sortable;
6799 * Returns the rendering (formatting) function defined for the column.
6800 * @param {Number} col The column index.
6801 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6803 getRenderer : function(col){
6804 if(!this.config[col].renderer){
6805 return Roo.grid.ColumnModel.defaultRenderer;
6807 return this.config[col].renderer;
6811 * Sets the rendering (formatting) function for a column.
6812 * @param {Number} col The column index
6813 * @param {Function} fn The function to use to process the cell's raw data
6814 * to return HTML markup for the grid view. The render function is called with
6815 * the following parameters:<ul>
6816 * <li>Data value.</li>
6817 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6818 * <li>css A CSS style string to apply to the table cell.</li>
6819 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6820 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6821 * <li>Row index</li>
6822 * <li>Column index</li>
6823 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6825 setRenderer : function(col, fn){
6826 this.config[col].renderer = fn;
6830 * Returns the width for the specified column.
6831 * @param {Number} col The column index
6834 getColumnWidth : function(col){
6835 return this.config[col].width * 1 || this.defaultWidth;
6839 * Sets the width for a column.
6840 * @param {Number} col The column index
6841 * @param {Number} width The new width
6843 setColumnWidth : function(col, width, suppressEvent){
6844 this.config[col].width = width;
6845 this.totalWidth = null;
6847 this.fireEvent("widthchange", this, col, width);
6852 * Returns the total width of all columns.
6853 * @param {Boolean} includeHidden True to include hidden column widths
6856 getTotalWidth : function(includeHidden){
6857 if(!this.totalWidth){
6858 this.totalWidth = 0;
6859 for(var i = 0, len = this.config.length; i < len; i++){
6860 if(includeHidden || !this.isHidden(i)){
6861 this.totalWidth += this.getColumnWidth(i);
6865 return this.totalWidth;
6869 * Returns the header for the specified column.
6870 * @param {Number} col The column index
6873 getColumnHeader : function(col){
6874 return this.config[col].header;
6878 * Sets the header for a column.
6879 * @param {Number} col The column index
6880 * @param {String} header The new header
6882 setColumnHeader : function(col, header){
6883 this.config[col].header = header;
6884 this.fireEvent("headerchange", this, col, header);
6888 * Returns the tooltip for the specified column.
6889 * @param {Number} col The column index
6892 getColumnTooltip : function(col){
6893 return this.config[col].tooltip;
6896 * Sets the tooltip for a column.
6897 * @param {Number} col The column index
6898 * @param {String} tooltip The new tooltip
6900 setColumnTooltip : function(col, tooltip){
6901 this.config[col].tooltip = tooltip;
6905 * Returns the dataIndex for the specified column.
6906 * @param {Number} col The column index
6909 getDataIndex : function(col){
6910 return this.config[col].dataIndex;
6914 * Sets the dataIndex for a column.
6915 * @param {Number} col The column index
6916 * @param {Number} dataIndex The new dataIndex
6918 setDataIndex : function(col, dataIndex){
6919 this.config[col].dataIndex = dataIndex;
6925 * Returns true if the cell is editable.
6926 * @param {Number} colIndex The column index
6927 * @param {Number} rowIndex The row index - this is nto actually used..?
6930 isCellEditable : function(colIndex, rowIndex){
6931 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6935 * Returns the editor defined for the cell/column.
6936 * return false or null to disable editing.
6937 * @param {Number} colIndex The column index
6938 * @param {Number} rowIndex The row index
6941 getCellEditor : function(colIndex, rowIndex){
6942 return this.config[colIndex].editor;
6946 * Sets if a column is editable.
6947 * @param {Number} col The column index
6948 * @param {Boolean} editable True if the column is editable
6950 setEditable : function(col, editable){
6951 this.config[col].editable = editable;
6956 * Returns true if the column is hidden.
6957 * @param {Number} colIndex The column index
6960 isHidden : function(colIndex){
6961 return this.config[colIndex].hidden;
6966 * Returns true if the column width cannot be changed
6968 isFixed : function(colIndex){
6969 return this.config[colIndex].fixed;
6973 * Returns true if the column can be resized
6976 isResizable : function(colIndex){
6977 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6980 * Sets if a column is hidden.
6981 * @param {Number} colIndex The column index
6982 * @param {Boolean} hidden True if the column is hidden
6984 setHidden : function(colIndex, hidden){
6985 this.config[colIndex].hidden = hidden;
6986 this.totalWidth = null;
6987 this.fireEvent("hiddenchange", this, colIndex, hidden);
6991 * Sets the editor for a column.
6992 * @param {Number} col The column index
6993 * @param {Object} editor The editor object
6995 setEditor : function(col, editor){
6996 this.config[col].editor = editor;
7000 Roo.grid.ColumnModel.defaultRenderer = function(value)
7002 if(typeof value == "object") {
7005 if(typeof value == "string" && value.length < 1){
7009 return String.format("{0}", value);
7012 // Alias for backwards compatibility
7013 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7016 * Ext JS Library 1.1.1
7017 * Copyright(c) 2006-2007, Ext JS, LLC.
7019 * Originally Released Under LGPL - original licence link has changed is not relivant.
7022 * <script type="text/javascript">
7026 * @class Roo.LoadMask
7027 * A simple utility class for generically masking elements while loading data. If the element being masked has
7028 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7029 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7030 * element's UpdateManager load indicator and will be destroyed after the initial load.
7032 * Create a new LoadMask
7033 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7034 * @param {Object} config The config object
7036 Roo.LoadMask = function(el, config){
7037 this.el = Roo.get(el);
7038 Roo.apply(this, config);
7040 this.store.on('beforeload', this.onBeforeLoad, this);
7041 this.store.on('load', this.onLoad, this);
7042 this.store.on('loadexception', this.onLoadException, this);
7043 this.removeMask = false;
7045 var um = this.el.getUpdateManager();
7046 um.showLoadIndicator = false; // disable the default indicator
7047 um.on('beforeupdate', this.onBeforeLoad, this);
7048 um.on('update', this.onLoad, this);
7049 um.on('failure', this.onLoad, this);
7050 this.removeMask = true;
7054 Roo.LoadMask.prototype = {
7056 * @cfg {Boolean} removeMask
7057 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7058 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7062 * The text to display in a centered loading message box (defaults to 'Loading...')
7066 * @cfg {String} msgCls
7067 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7069 msgCls : 'x-mask-loading',
7072 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7078 * Disables the mask to prevent it from being displayed
7080 disable : function(){
7081 this.disabled = true;
7085 * Enables the mask so that it can be displayed
7087 enable : function(){
7088 this.disabled = false;
7091 onLoadException : function()
7095 if (typeof(arguments[3]) != 'undefined') {
7096 Roo.MessageBox.alert("Error loading",arguments[3]);
7100 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7101 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7108 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7113 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7117 onBeforeLoad : function(){
7119 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7124 destroy : function(){
7126 this.store.un('beforeload', this.onBeforeLoad, this);
7127 this.store.un('load', this.onLoad, this);
7128 this.store.un('loadexception', this.onLoadException, this);
7130 var um = this.el.getUpdateManager();
7131 um.un('beforeupdate', this.onBeforeLoad, this);
7132 um.un('update', this.onLoad, this);
7133 um.un('failure', this.onLoad, this);
7144 * @class Roo.bootstrap.Table
7145 * @extends Roo.bootstrap.Component
7146 * Bootstrap Table class
7147 * @cfg {String} cls table class
7148 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7149 * @cfg {String} bgcolor Specifies the background color for a table
7150 * @cfg {Number} border Specifies whether the table cells should have borders or not
7151 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7152 * @cfg {Number} cellspacing Specifies the space between cells
7153 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7154 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7155 * @cfg {String} sortable Specifies that the table should be sortable
7156 * @cfg {String} summary Specifies a summary of the content of a table
7157 * @cfg {Number} width Specifies the width of a table
7158 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7160 * @cfg {boolean} striped Should the rows be alternative striped
7161 * @cfg {boolean} bordered Add borders to the table
7162 * @cfg {boolean} hover Add hover highlighting
7163 * @cfg {boolean} condensed Format condensed
7164 * @cfg {boolean} responsive Format condensed
7165 * @cfg {Boolean} loadMask (true|false) default false
7166 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7167 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7168 * @cfg {Boolean} rowSelection (true|false) default false
7169 * @cfg {Boolean} cellSelection (true|false) default false
7170 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7171 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7172 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7173 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7177 * Create a new Table
7178 * @param {Object} config The config object
7181 Roo.bootstrap.Table = function(config){
7182 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7187 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7188 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7189 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7190 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7192 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7194 this.sm.grid = this;
7195 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7196 this.sm = this.selModel;
7197 this.sm.xmodule = this.xmodule || false;
7200 if (this.cm && typeof(this.cm.config) == 'undefined') {
7201 this.colModel = new Roo.grid.ColumnModel(this.cm);
7202 this.cm = this.colModel;
7203 this.cm.xmodule = this.xmodule || false;
7206 this.store= Roo.factory(this.store, Roo.data);
7207 this.ds = this.store;
7208 this.ds.xmodule = this.xmodule || false;
7211 if (this.footer && this.store) {
7212 this.footer.dataSource = this.ds;
7213 this.footer = Roo.factory(this.footer);
7220 * Fires when a cell is clicked
7221 * @param {Roo.bootstrap.Table} this
7222 * @param {Roo.Element} el
7223 * @param {Number} rowIndex
7224 * @param {Number} columnIndex
7225 * @param {Roo.EventObject} e
7229 * @event celldblclick
7230 * Fires when a cell is double clicked
7231 * @param {Roo.bootstrap.Table} this
7232 * @param {Roo.Element} el
7233 * @param {Number} rowIndex
7234 * @param {Number} columnIndex
7235 * @param {Roo.EventObject} e
7237 "celldblclick" : true,
7240 * Fires when a row is clicked
7241 * @param {Roo.bootstrap.Table} this
7242 * @param {Roo.Element} el
7243 * @param {Number} rowIndex
7244 * @param {Roo.EventObject} e
7248 * @event rowdblclick
7249 * Fires when a row is double clicked
7250 * @param {Roo.bootstrap.Table} this
7251 * @param {Roo.Element} el
7252 * @param {Number} rowIndex
7253 * @param {Roo.EventObject} e
7255 "rowdblclick" : true,
7258 * Fires when a mouseover occur
7259 * @param {Roo.bootstrap.Table} this
7260 * @param {Roo.Element} el
7261 * @param {Number} rowIndex
7262 * @param {Number} columnIndex
7263 * @param {Roo.EventObject} e
7268 * Fires when a mouseout occur
7269 * @param {Roo.bootstrap.Table} this
7270 * @param {Roo.Element} el
7271 * @param {Number} rowIndex
7272 * @param {Number} columnIndex
7273 * @param {Roo.EventObject} e
7278 * Fires when a row is rendered, so you can change add a style to it.
7279 * @param {Roo.bootstrap.Table} this
7280 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7284 * @event rowsrendered
7285 * Fires when all the rows have been rendered
7286 * @param {Roo.bootstrap.Table} this
7288 'rowsrendered' : true,
7290 * @event contextmenu
7291 * The raw contextmenu event for the entire grid.
7292 * @param {Roo.EventObject} e
7294 "contextmenu" : true,
7296 * @event rowcontextmenu
7297 * Fires when a row is right clicked
7298 * @param {Roo.bootstrap.Table} this
7299 * @param {Number} rowIndex
7300 * @param {Roo.EventObject} e
7302 "rowcontextmenu" : true,
7304 * @event cellcontextmenu
7305 * Fires when a cell is right clicked
7306 * @param {Roo.bootstrap.Table} this
7307 * @param {Number} rowIndex
7308 * @param {Number} cellIndex
7309 * @param {Roo.EventObject} e
7311 "cellcontextmenu" : true,
7313 * @event headercontextmenu
7314 * Fires when a header is right clicked
7315 * @param {Roo.bootstrap.Table} this
7316 * @param {Number} columnIndex
7317 * @param {Roo.EventObject} e
7319 "headercontextmenu" : true
7323 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7349 rowSelection : false,
7350 cellSelection : false,
7353 // Roo.Element - the tbody
7355 // Roo.Element - thead element
7358 container: false, // used by gridpanel...
7364 auto_hide_footer : false,
7366 getAutoCreate : function()
7368 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7375 if (this.scrollBody) {
7376 cfg.cls += ' table-body-fixed';
7379 cfg.cls += ' table-striped';
7383 cfg.cls += ' table-hover';
7385 if (this.bordered) {
7386 cfg.cls += ' table-bordered';
7388 if (this.condensed) {
7389 cfg.cls += ' table-condensed';
7391 if (this.responsive) {
7392 cfg.cls += ' table-responsive';
7396 cfg.cls+= ' ' +this.cls;
7399 // this lot should be simplifed...
7412 ].forEach(function(k) {
7420 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7423 if(this.store || this.cm){
7424 if(this.headerShow){
7425 cfg.cn.push(this.renderHeader());
7428 cfg.cn.push(this.renderBody());
7430 if(this.footerShow){
7431 cfg.cn.push(this.renderFooter());
7433 // where does this come from?
7434 //cfg.cls+= ' TableGrid';
7437 return { cn : [ cfg ] };
7440 initEvents : function()
7442 if(!this.store || !this.cm){
7445 if (this.selModel) {
7446 this.selModel.initEvents();
7450 //Roo.log('initEvents with ds!!!!');
7452 this.mainBody = this.el.select('tbody', true).first();
7453 this.mainHead = this.el.select('thead', true).first();
7454 this.mainFoot = this.el.select('tfoot', true).first();
7460 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7461 e.on('click', _this.sort, _this);
7464 this.mainBody.on("click", this.onClick, this);
7465 this.mainBody.on("dblclick", this.onDblClick, this);
7467 // why is this done????? = it breaks dialogs??
7468 //this.parent().el.setStyle('position', 'relative');
7472 this.footer.parentId = this.id;
7473 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7476 this.el.select('tfoot tr td').first().addClass('hide');
7481 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7484 this.store.on('load', this.onLoad, this);
7485 this.store.on('beforeload', this.onBeforeLoad, this);
7486 this.store.on('update', this.onUpdate, this);
7487 this.store.on('add', this.onAdd, this);
7488 this.store.on("clear", this.clear, this);
7490 this.el.on("contextmenu", this.onContextMenu, this);
7492 this.mainBody.on('scroll', this.onBodyScroll, this);
7494 this.cm.on("headerchange", this.onHeaderChange, this);
7496 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7500 onContextMenu : function(e, t)
7502 this.processEvent("contextmenu", e);
7505 processEvent : function(name, e)
7507 if (name != 'touchstart' ) {
7508 this.fireEvent(name, e);
7511 var t = e.getTarget();
7513 var cell = Roo.get(t);
7519 if(cell.findParent('tfoot', false, true)){
7523 if(cell.findParent('thead', false, true)){
7525 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7526 cell = Roo.get(t).findParent('th', false, true);
7528 Roo.log("failed to find th in thead?");
7529 Roo.log(e.getTarget());
7534 var cellIndex = cell.dom.cellIndex;
7536 var ename = name == 'touchstart' ? 'click' : name;
7537 this.fireEvent("header" + ename, this, cellIndex, e);
7542 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7543 cell = Roo.get(t).findParent('td', false, true);
7545 Roo.log("failed to find th in tbody?");
7546 Roo.log(e.getTarget());
7551 var row = cell.findParent('tr', false, true);
7552 var cellIndex = cell.dom.cellIndex;
7553 var rowIndex = row.dom.rowIndex - 1;
7557 this.fireEvent("row" + name, this, rowIndex, e);
7561 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7567 onMouseover : function(e, el)
7569 var cell = Roo.get(el);
7575 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7576 cell = cell.findParent('td', false, true);
7579 var row = cell.findParent('tr', false, true);
7580 var cellIndex = cell.dom.cellIndex;
7581 var rowIndex = row.dom.rowIndex - 1; // start from 0
7583 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7587 onMouseout : function(e, el)
7589 var cell = Roo.get(el);
7595 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7596 cell = cell.findParent('td', false, true);
7599 var row = cell.findParent('tr', false, true);
7600 var cellIndex = cell.dom.cellIndex;
7601 var rowIndex = row.dom.rowIndex - 1; // start from 0
7603 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7607 onClick : function(e, el)
7609 var cell = Roo.get(el);
7611 if(!cell || (!this.cellSelection && !this.rowSelection)){
7615 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7616 cell = cell.findParent('td', false, true);
7619 if(!cell || typeof(cell) == 'undefined'){
7623 var row = cell.findParent('tr', false, true);
7625 if(!row || typeof(row) == 'undefined'){
7629 var cellIndex = cell.dom.cellIndex;
7630 var rowIndex = this.getRowIndex(row);
7632 // why??? - should these not be based on SelectionModel?
7633 if(this.cellSelection){
7634 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7637 if(this.rowSelection){
7638 this.fireEvent('rowclick', this, row, rowIndex, e);
7644 onDblClick : function(e,el)
7646 var cell = Roo.get(el);
7648 if(!cell || (!this.cellSelection && !this.rowSelection)){
7652 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7653 cell = cell.findParent('td', false, true);
7656 if(!cell || typeof(cell) == 'undefined'){
7660 var row = cell.findParent('tr', false, true);
7662 if(!row || typeof(row) == 'undefined'){
7666 var cellIndex = cell.dom.cellIndex;
7667 var rowIndex = this.getRowIndex(row);
7669 if(this.cellSelection){
7670 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7673 if(this.rowSelection){
7674 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7678 sort : function(e,el)
7680 var col = Roo.get(el);
7682 if(!col.hasClass('sortable')){
7686 var sort = col.attr('sort');
7689 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7693 this.store.sortInfo = {field : sort, direction : dir};
7696 Roo.log("calling footer first");
7697 this.footer.onClick('first');
7700 this.store.load({ params : { start : 0 } });
7704 renderHeader : function()
7712 this.totalWidth = 0;
7714 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7716 var config = cm.config[i];
7720 cls : 'x-hcol-' + i,
7722 html: cm.getColumnHeader(i)
7727 if(typeof(config.sortable) != 'undefined' && config.sortable){
7729 c.html = '<i class="glyphicon"></i>' + c.html;
7732 // could use BS4 hidden-..-down
7734 if(typeof(config.lgHeader) != 'undefined'){
7735 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7738 if(typeof(config.mdHeader) != 'undefined'){
7739 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7742 if(typeof(config.smHeader) != 'undefined'){
7743 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7746 if(typeof(config.xsHeader) != 'undefined'){
7747 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7754 if(typeof(config.tooltip) != 'undefined'){
7755 c.tooltip = config.tooltip;
7758 if(typeof(config.colspan) != 'undefined'){
7759 c.colspan = config.colspan;
7762 if(typeof(config.hidden) != 'undefined' && config.hidden){
7763 c.style += ' display:none;';
7766 if(typeof(config.dataIndex) != 'undefined'){
7767 c.sort = config.dataIndex;
7772 if(typeof(config.align) != 'undefined' && config.align.length){
7773 c.style += ' text-align:' + config.align + ';';
7776 if(typeof(config.width) != 'undefined'){
7777 c.style += ' width:' + config.width + 'px;';
7778 this.totalWidth += config.width;
7780 this.totalWidth += 100; // assume minimum of 100 per column?
7783 if(typeof(config.cls) != 'undefined'){
7784 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7787 ['xs','sm','md','lg'].map(function(size){
7789 if(typeof(config[size]) == 'undefined'){
7793 if (!config[size]) { // 0 = hidden
7794 // BS 4 '0' is treated as hide that column and below.
7795 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7799 c.cls += ' col-' + size + '-' + config[size] + (
7800 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7812 renderBody : function()
7822 colspan : this.cm.getColumnCount()
7832 renderFooter : function()
7842 colspan : this.cm.getColumnCount()
7856 // Roo.log('ds onload');
7861 var ds = this.store;
7863 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7864 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7865 if (_this.store.sortInfo) {
7867 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7868 e.select('i', true).addClass(['glyphicon-arrow-up']);
7871 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7872 e.select('i', true).addClass(['glyphicon-arrow-down']);
7877 var tbody = this.mainBody;
7879 if(ds.getCount() > 0){
7880 ds.data.each(function(d,rowIndex){
7881 var row = this.renderRow(cm, ds, rowIndex);
7883 tbody.createChild(row);
7887 if(row.cellObjects.length){
7888 Roo.each(row.cellObjects, function(r){
7889 _this.renderCellObject(r);
7896 var tfoot = this.el.select('tfoot', true).first();
7898 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7900 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7902 var total = this.ds.getTotalCount();
7904 if(this.footer.pageSize < total){
7905 this.mainFoot.show();
7909 Roo.each(this.el.select('tbody td', true).elements, function(e){
7910 e.on('mouseover', _this.onMouseover, _this);
7913 Roo.each(this.el.select('tbody td', true).elements, function(e){
7914 e.on('mouseout', _this.onMouseout, _this);
7916 this.fireEvent('rowsrendered', this);
7922 onUpdate : function(ds,record)
7924 this.refreshRow(record);
7928 onRemove : function(ds, record, index, isUpdate){
7929 if(isUpdate !== true){
7930 this.fireEvent("beforerowremoved", this, index, record);
7932 var bt = this.mainBody.dom;
7934 var rows = this.el.select('tbody > tr', true).elements;
7936 if(typeof(rows[index]) != 'undefined'){
7937 bt.removeChild(rows[index].dom);
7940 // if(bt.rows[index]){
7941 // bt.removeChild(bt.rows[index]);
7944 if(isUpdate !== true){
7945 //this.stripeRows(index);
7946 //this.syncRowHeights(index, index);
7948 this.fireEvent("rowremoved", this, index, record);
7952 onAdd : function(ds, records, rowIndex)
7954 //Roo.log('on Add called');
7955 // - note this does not handle multiple adding very well..
7956 var bt = this.mainBody.dom;
7957 for (var i =0 ; i < records.length;i++) {
7958 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7959 //Roo.log(records[i]);
7960 //Roo.log(this.store.getAt(rowIndex+i));
7961 this.insertRow(this.store, rowIndex + i, false);
7968 refreshRow : function(record){
7969 var ds = this.store, index;
7970 if(typeof record == 'number'){
7972 record = ds.getAt(index);
7974 index = ds.indexOf(record);
7976 this.insertRow(ds, index, true);
7978 this.onRemove(ds, record, index+1, true);
7980 //this.syncRowHeights(index, index);
7982 this.fireEvent("rowupdated", this, index, record);
7985 insertRow : function(dm, rowIndex, isUpdate){
7988 this.fireEvent("beforerowsinserted", this, rowIndex);
7990 //var s = this.getScrollState();
7991 var row = this.renderRow(this.cm, this.store, rowIndex);
7992 // insert before rowIndex..
7993 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7997 if(row.cellObjects.length){
7998 Roo.each(row.cellObjects, function(r){
7999 _this.renderCellObject(r);
8004 this.fireEvent("rowsinserted", this, rowIndex);
8005 //this.syncRowHeights(firstRow, lastRow);
8006 //this.stripeRows(firstRow);
8013 getRowDom : function(rowIndex)
8015 var rows = this.el.select('tbody > tr', true).elements;
8017 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8020 // returns the object tree for a tr..
8023 renderRow : function(cm, ds, rowIndex)
8025 var d = ds.getAt(rowIndex);
8029 cls : 'x-row-' + rowIndex,
8033 var cellObjects = [];
8035 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8036 var config = cm.config[i];
8038 var renderer = cm.getRenderer(i);
8042 if(typeof(renderer) !== 'undefined'){
8043 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8045 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8046 // and are rendered into the cells after the row is rendered - using the id for the element.
8048 if(typeof(value) === 'object'){
8058 rowIndex : rowIndex,
8063 this.fireEvent('rowclass', this, rowcfg);
8067 cls : rowcfg.rowClass + ' x-col-' + i,
8069 html: (typeof(value) === 'object') ? '' : value
8076 if(typeof(config.colspan) != 'undefined'){
8077 td.colspan = config.colspan;
8080 if(typeof(config.hidden) != 'undefined' && config.hidden){
8081 td.style += ' display:none;';
8084 if(typeof(config.align) != 'undefined' && config.align.length){
8085 td.style += ' text-align:' + config.align + ';';
8087 if(typeof(config.valign) != 'undefined' && config.valign.length){
8088 td.style += ' vertical-align:' + config.valign + ';';
8091 if(typeof(config.width) != 'undefined'){
8092 td.style += ' width:' + config.width + 'px;';
8095 if(typeof(config.cursor) != 'undefined'){
8096 td.style += ' cursor:' + config.cursor + ';';
8099 if(typeof(config.cls) != 'undefined'){
8100 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8103 ['xs','sm','md','lg'].map(function(size){
8105 if(typeof(config[size]) == 'undefined'){
8111 if (!config[size]) { // 0 = hidden
8112 // BS 4 '0' is treated as hide that column and below.
8113 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8117 td.cls += ' col-' + size + '-' + config[size] + (
8118 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8128 row.cellObjects = cellObjects;
8136 onBeforeLoad : function()
8145 this.el.select('tbody', true).first().dom.innerHTML = '';
8148 * Show or hide a row.
8149 * @param {Number} rowIndex to show or hide
8150 * @param {Boolean} state hide
8152 setRowVisibility : function(rowIndex, state)
8154 var bt = this.mainBody.dom;
8156 var rows = this.el.select('tbody > tr', true).elements;
8158 if(typeof(rows[rowIndex]) == 'undefined'){
8161 rows[rowIndex].dom.style.display = state ? '' : 'none';
8165 getSelectionModel : function(){
8167 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8169 return this.selModel;
8172 * Render the Roo.bootstrap object from renderder
8174 renderCellObject : function(r)
8178 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8180 var t = r.cfg.render(r.container);
8183 Roo.each(r.cfg.cn, function(c){
8185 container: t.getChildContainer(),
8188 _this.renderCellObject(child);
8193 getRowIndex : function(row)
8197 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8208 * Returns the grid's underlying element = used by panel.Grid
8209 * @return {Element} The element
8211 getGridEl : function(){
8215 * Forces a resize - used by panel.Grid
8216 * @return {Element} The element
8218 autoSize : function()
8220 //var ctr = Roo.get(this.container.dom.parentElement);
8221 var ctr = Roo.get(this.el.dom);
8223 var thd = this.getGridEl().select('thead',true).first();
8224 var tbd = this.getGridEl().select('tbody', true).first();
8225 var tfd = this.getGridEl().select('tfoot', true).first();
8227 var cw = ctr.getWidth();
8231 tbd.setWidth(ctr.getWidth());
8232 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8233 // this needs fixing for various usage - currently only hydra job advers I think..
8235 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8237 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8240 cw = Math.max(cw, this.totalWidth);
8241 this.getGridEl().select('tr',true).setWidth(cw);
8242 // resize 'expandable coloumn?
8244 return; // we doe not have a view in this design..
8247 onBodyScroll: function()
8249 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8251 this.mainHead.setStyle({
8252 'position' : 'relative',
8253 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8259 var scrollHeight = this.mainBody.dom.scrollHeight;
8261 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8263 var height = this.mainBody.getHeight();
8265 if(scrollHeight - height == scrollTop) {
8267 var total = this.ds.getTotalCount();
8269 if(this.footer.cursor + this.footer.pageSize < total){
8271 this.footer.ds.load({
8273 start : this.footer.cursor + this.footer.pageSize,
8274 limit : this.footer.pageSize
8284 onHeaderChange : function()
8286 var header = this.renderHeader();
8287 var table = this.el.select('table', true).first();
8289 this.mainHead.remove();
8290 this.mainHead = table.createChild(header, this.mainBody, false);
8293 onHiddenChange : function(colModel, colIndex, hidden)
8295 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8296 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8298 this.CSS.updateRule(thSelector, "display", "");
8299 this.CSS.updateRule(tdSelector, "display", "");
8302 this.CSS.updateRule(thSelector, "display", "none");
8303 this.CSS.updateRule(tdSelector, "display", "none");
8306 this.onHeaderChange();
8310 setColumnWidth: function(col_index, width)
8312 // width = "md-2 xs-2..."
8313 if(!this.colModel.config[col_index]) {
8317 var w = width.split(" ");
8319 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8321 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8324 for(var j = 0; j < w.length; j++) {
8330 var size_cls = w[j].split("-");
8332 if(!Number.isInteger(size_cls[1] * 1)) {
8336 if(!this.colModel.config[col_index][size_cls[0]]) {
8340 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8344 h_row[0].classList.replace(
8345 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8346 "col-"+size_cls[0]+"-"+size_cls[1]
8349 for(var i = 0; i < rows.length; i++) {
8351 var size_cls = w[j].split("-");
8353 if(!Number.isInteger(size_cls[1] * 1)) {
8357 if(!this.colModel.config[col_index][size_cls[0]]) {
8361 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8365 rows[i].classList.replace(
8366 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8367 "col-"+size_cls[0]+"-"+size_cls[1]
8371 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8386 * @class Roo.bootstrap.TableCell
8387 * @extends Roo.bootstrap.Component
8388 * Bootstrap TableCell class
8389 * @cfg {String} html cell contain text
8390 * @cfg {String} cls cell class
8391 * @cfg {String} tag cell tag (td|th) default td
8392 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8393 * @cfg {String} align Aligns the content in a cell
8394 * @cfg {String} axis Categorizes cells
8395 * @cfg {String} bgcolor Specifies the background color of a cell
8396 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8397 * @cfg {Number} colspan Specifies the number of columns a cell should span
8398 * @cfg {String} headers Specifies one or more header cells a cell is related to
8399 * @cfg {Number} height Sets the height of a cell
8400 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8401 * @cfg {Number} rowspan Sets the number of rows a cell should span
8402 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8403 * @cfg {String} valign Vertical aligns the content in a cell
8404 * @cfg {Number} width Specifies the width of a cell
8407 * Create a new TableCell
8408 * @param {Object} config The config object
8411 Roo.bootstrap.TableCell = function(config){
8412 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8415 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8435 getAutoCreate : function(){
8436 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8456 cfg.align=this.align
8462 cfg.bgcolor=this.bgcolor
8465 cfg.charoff=this.charoff
8468 cfg.colspan=this.colspan
8471 cfg.headers=this.headers
8474 cfg.height=this.height
8477 cfg.nowrap=this.nowrap
8480 cfg.rowspan=this.rowspan
8483 cfg.scope=this.scope
8486 cfg.valign=this.valign
8489 cfg.width=this.width
8508 * @class Roo.bootstrap.TableRow
8509 * @extends Roo.bootstrap.Component
8510 * Bootstrap TableRow class
8511 * @cfg {String} cls row class
8512 * @cfg {String} align Aligns the content in a table row
8513 * @cfg {String} bgcolor Specifies a background color for a table row
8514 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8515 * @cfg {String} valign Vertical aligns the content in a table row
8518 * Create a new TableRow
8519 * @param {Object} config The config object
8522 Roo.bootstrap.TableRow = function(config){
8523 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8526 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8534 getAutoCreate : function(){
8535 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8545 cfg.align = this.align;
8548 cfg.bgcolor = this.bgcolor;
8551 cfg.charoff = this.charoff;
8554 cfg.valign = this.valign;
8572 * @class Roo.bootstrap.TableBody
8573 * @extends Roo.bootstrap.Component
8574 * Bootstrap TableBody class
8575 * @cfg {String} cls element class
8576 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8577 * @cfg {String} align Aligns the content inside the element
8578 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8579 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8582 * Create a new TableBody
8583 * @param {Object} config The config object
8586 Roo.bootstrap.TableBody = function(config){
8587 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8590 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8598 getAutoCreate : function(){
8599 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8613 cfg.align = this.align;
8616 cfg.charoff = this.charoff;
8619 cfg.valign = this.valign;
8626 // initEvents : function()
8633 // this.store = Roo.factory(this.store, Roo.data);
8634 // this.store.on('load', this.onLoad, this);
8636 // this.store.load();
8640 // onLoad: function ()
8642 // this.fireEvent('load', this);
8652 * Ext JS Library 1.1.1
8653 * Copyright(c) 2006-2007, Ext JS, LLC.
8655 * Originally Released Under LGPL - original licence link has changed is not relivant.
8658 * <script type="text/javascript">
8661 // as we use this in bootstrap.
8662 Roo.namespace('Roo.form');
8664 * @class Roo.form.Action
8665 * Internal Class used to handle form actions
8667 * @param {Roo.form.BasicForm} el The form element or its id
8668 * @param {Object} config Configuration options
8673 // define the action interface
8674 Roo.form.Action = function(form, options){
8676 this.options = options || {};
8679 * Client Validation Failed
8682 Roo.form.Action.CLIENT_INVALID = 'client';
8684 * Server Validation Failed
8687 Roo.form.Action.SERVER_INVALID = 'server';
8689 * Connect to Server Failed
8692 Roo.form.Action.CONNECT_FAILURE = 'connect';
8694 * Reading Data from Server Failed
8697 Roo.form.Action.LOAD_FAILURE = 'load';
8699 Roo.form.Action.prototype = {
8701 failureType : undefined,
8702 response : undefined,
8706 run : function(options){
8711 success : function(response){
8716 handleResponse : function(response){
8720 // default connection failure
8721 failure : function(response){
8723 this.response = response;
8724 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8725 this.form.afterAction(this, false);
8728 processResponse : function(response){
8729 this.response = response;
8730 if(!response.responseText){
8733 this.result = this.handleResponse(response);
8737 // utility functions used internally
8738 getUrl : function(appendParams){
8739 var url = this.options.url || this.form.url || this.form.el.dom.action;
8741 var p = this.getParams();
8743 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8749 getMethod : function(){
8750 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8753 getParams : function(){
8754 var bp = this.form.baseParams;
8755 var p = this.options.params;
8757 if(typeof p == "object"){
8758 p = Roo.urlEncode(Roo.applyIf(p, bp));
8759 }else if(typeof p == 'string' && bp){
8760 p += '&' + Roo.urlEncode(bp);
8763 p = Roo.urlEncode(bp);
8768 createCallback : function(){
8770 success: this.success,
8771 failure: this.failure,
8773 timeout: (this.form.timeout*1000),
8774 upload: this.form.fileUpload ? this.success : undefined
8779 Roo.form.Action.Submit = function(form, options){
8780 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8783 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8786 haveProgress : false,
8787 uploadComplete : false,
8789 // uploadProgress indicator.
8790 uploadProgress : function()
8792 if (!this.form.progressUrl) {
8796 if (!this.haveProgress) {
8797 Roo.MessageBox.progress("Uploading", "Uploading");
8799 if (this.uploadComplete) {
8800 Roo.MessageBox.hide();
8804 this.haveProgress = true;
8806 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8808 var c = new Roo.data.Connection();
8810 url : this.form.progressUrl,
8815 success : function(req){
8816 //console.log(data);
8820 rdata = Roo.decode(req.responseText)
8822 Roo.log("Invalid data from server..");
8826 if (!rdata || !rdata.success) {
8828 Roo.MessageBox.alert(Roo.encode(rdata));
8831 var data = rdata.data;
8833 if (this.uploadComplete) {
8834 Roo.MessageBox.hide();
8839 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8840 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8843 this.uploadProgress.defer(2000,this);
8846 failure: function(data) {
8847 Roo.log('progress url failed ');
8858 // run get Values on the form, so it syncs any secondary forms.
8859 this.form.getValues();
8861 var o = this.options;
8862 var method = this.getMethod();
8863 var isPost = method == 'POST';
8864 if(o.clientValidation === false || this.form.isValid()){
8866 if (this.form.progressUrl) {
8867 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8868 (new Date() * 1) + '' + Math.random());
8873 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8874 form:this.form.el.dom,
8875 url:this.getUrl(!isPost),
8877 params:isPost ? this.getParams() : null,
8878 isUpload: this.form.fileUpload,
8879 formData : this.form.formData
8882 this.uploadProgress();
8884 }else if (o.clientValidation !== false){ // client validation failed
8885 this.failureType = Roo.form.Action.CLIENT_INVALID;
8886 this.form.afterAction(this, false);
8890 success : function(response)
8892 this.uploadComplete= true;
8893 if (this.haveProgress) {
8894 Roo.MessageBox.hide();
8898 var result = this.processResponse(response);
8899 if(result === true || result.success){
8900 this.form.afterAction(this, true);
8904 this.form.markInvalid(result.errors);
8905 this.failureType = Roo.form.Action.SERVER_INVALID;
8907 this.form.afterAction(this, false);
8909 failure : function(response)
8911 this.uploadComplete= true;
8912 if (this.haveProgress) {
8913 Roo.MessageBox.hide();
8916 this.response = response;
8917 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8918 this.form.afterAction(this, false);
8921 handleResponse : function(response){
8922 if(this.form.errorReader){
8923 var rs = this.form.errorReader.read(response);
8926 for(var i = 0, len = rs.records.length; i < len; i++) {
8927 var r = rs.records[i];
8931 if(errors.length < 1){
8935 success : rs.success,
8941 ret = Roo.decode(response.responseText);
8945 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8955 Roo.form.Action.Load = function(form, options){
8956 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8957 this.reader = this.form.reader;
8960 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8965 Roo.Ajax.request(Roo.apply(
8966 this.createCallback(), {
8967 method:this.getMethod(),
8968 url:this.getUrl(false),
8969 params:this.getParams()
8973 success : function(response){
8975 var result = this.processResponse(response);
8976 if(result === true || !result.success || !result.data){
8977 this.failureType = Roo.form.Action.LOAD_FAILURE;
8978 this.form.afterAction(this, false);
8981 this.form.clearInvalid();
8982 this.form.setValues(result.data);
8983 this.form.afterAction(this, true);
8986 handleResponse : function(response){
8987 if(this.form.reader){
8988 var rs = this.form.reader.read(response);
8989 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8991 success : rs.success,
8995 return Roo.decode(response.responseText);
8999 Roo.form.Action.ACTION_TYPES = {
9000 'load' : Roo.form.Action.Load,
9001 'submit' : Roo.form.Action.Submit
9010 * @class Roo.bootstrap.Form
9011 * @extends Roo.bootstrap.Component
9012 * Bootstrap Form class
9013 * @cfg {String} method GET | POST (default POST)
9014 * @cfg {String} labelAlign top | left (default top)
9015 * @cfg {String} align left | right - for navbars
9016 * @cfg {Boolean} loadMask load mask when submit (default true)
9021 * @param {Object} config The config object
9025 Roo.bootstrap.Form = function(config){
9027 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9029 Roo.bootstrap.Form.popover.apply();
9033 * @event clientvalidation
9034 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9035 * @param {Form} this
9036 * @param {Boolean} valid true if the form has passed client-side validation
9038 clientvalidation: true,
9040 * @event beforeaction
9041 * Fires before any action is performed. Return false to cancel the action.
9042 * @param {Form} this
9043 * @param {Action} action The action to be performed
9047 * @event actionfailed
9048 * Fires when an action fails.
9049 * @param {Form} this
9050 * @param {Action} action The action that failed
9052 actionfailed : true,
9054 * @event actioncomplete
9055 * Fires when an action is completed.
9056 * @param {Form} this
9057 * @param {Action} action The action that completed
9059 actioncomplete : true
9063 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9066 * @cfg {String} method
9067 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9072 * The URL to use for form actions if one isn't supplied in the action options.
9075 * @cfg {Boolean} fileUpload
9076 * Set to true if this form is a file upload.
9080 * @cfg {Object} baseParams
9081 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9085 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9089 * @cfg {Sting} align (left|right) for navbar forms
9094 activeAction : null,
9097 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9098 * element by passing it or its id or mask the form itself by passing in true.
9101 waitMsgTarget : false,
9106 * @cfg {Boolean} errorMask (true|false) default false
9111 * @cfg {Number} maskOffset Default 100
9116 * @cfg {Boolean} maskBody
9120 getAutoCreate : function(){
9124 method : this.method || 'POST',
9125 id : this.id || Roo.id(),
9128 if (this.parent().xtype.match(/^Nav/)) {
9129 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9133 if (this.labelAlign == 'left' ) {
9134 cfg.cls += ' form-horizontal';
9140 initEvents : function()
9142 this.el.on('submit', this.onSubmit, this);
9143 // this was added as random key presses on the form where triggering form submit.
9144 this.el.on('keypress', function(e) {
9145 if (e.getCharCode() != 13) {
9148 // we might need to allow it for textareas.. and some other items.
9149 // check e.getTarget().
9151 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9155 Roo.log("keypress blocked");
9163 onSubmit : function(e){
9168 * Returns true if client-side validation on the form is successful.
9171 isValid : function(){
9172 var items = this.getItems();
9176 items.each(function(f){
9182 Roo.log('invalid field: ' + f.name);
9186 if(!target && f.el.isVisible(true)){
9192 if(this.errorMask && !valid){
9193 Roo.bootstrap.Form.popover.mask(this, target);
9200 * Returns true if any fields in this form have changed since their original load.
9203 isDirty : function(){
9205 var items = this.getItems();
9206 items.each(function(f){
9216 * Performs a predefined action (submit or load) or custom actions you define on this form.
9217 * @param {String} actionName The name of the action type
9218 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9219 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9220 * accept other config options):
9222 Property Type Description
9223 ---------------- --------------- ----------------------------------------------------------------------------------
9224 url String The url for the action (defaults to the form's url)
9225 method String The form method to use (defaults to the form's method, or POST if not defined)
9226 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9227 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9228 validate the form on the client (defaults to false)
9230 * @return {BasicForm} this
9232 doAction : function(action, options){
9233 if(typeof action == 'string'){
9234 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9236 if(this.fireEvent('beforeaction', this, action) !== false){
9237 this.beforeAction(action);
9238 action.run.defer(100, action);
9244 beforeAction : function(action){
9245 var o = action.options;
9250 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9252 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9255 // not really supported yet.. ??
9257 //if(this.waitMsgTarget === true){
9258 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9259 //}else if(this.waitMsgTarget){
9260 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9261 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9263 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9269 afterAction : function(action, success){
9270 this.activeAction = null;
9271 var o = action.options;
9276 Roo.get(document.body).unmask();
9282 //if(this.waitMsgTarget === true){
9283 // this.el.unmask();
9284 //}else if(this.waitMsgTarget){
9285 // this.waitMsgTarget.unmask();
9287 // Roo.MessageBox.updateProgress(1);
9288 // Roo.MessageBox.hide();
9295 Roo.callback(o.success, o.scope, [this, action]);
9296 this.fireEvent('actioncomplete', this, action);
9300 // failure condition..
9301 // we have a scenario where updates need confirming.
9302 // eg. if a locking scenario exists..
9303 // we look for { errors : { needs_confirm : true }} in the response.
9305 (typeof(action.result) != 'undefined') &&
9306 (typeof(action.result.errors) != 'undefined') &&
9307 (typeof(action.result.errors.needs_confirm) != 'undefined')
9310 Roo.log("not supported yet");
9313 Roo.MessageBox.confirm(
9314 "Change requires confirmation",
9315 action.result.errorMsg,
9320 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9330 Roo.callback(o.failure, o.scope, [this, action]);
9331 // show an error message if no failed handler is set..
9332 if (!this.hasListener('actionfailed')) {
9333 Roo.log("need to add dialog support");
9335 Roo.MessageBox.alert("Error",
9336 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9337 action.result.errorMsg :
9338 "Saving Failed, please check your entries or try again"
9343 this.fireEvent('actionfailed', this, action);
9348 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9349 * @param {String} id The value to search for
9352 findField : function(id){
9353 var items = this.getItems();
9354 var field = items.get(id);
9356 items.each(function(f){
9357 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9364 return field || null;
9367 * Mark fields in this form invalid in bulk.
9368 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9369 * @return {BasicForm} this
9371 markInvalid : function(errors){
9372 if(errors instanceof Array){
9373 for(var i = 0, len = errors.length; i < len; i++){
9374 var fieldError = errors[i];
9375 var f = this.findField(fieldError.id);
9377 f.markInvalid(fieldError.msg);
9383 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9384 field.markInvalid(errors[id]);
9388 //Roo.each(this.childForms || [], function (f) {
9389 // f.markInvalid(errors);
9396 * Set values for fields in this form in bulk.
9397 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9398 * @return {BasicForm} this
9400 setValues : function(values){
9401 if(values instanceof Array){ // array of objects
9402 for(var i = 0, len = values.length; i < len; i++){
9404 var f = this.findField(v.id);
9406 f.setValue(v.value);
9407 if(this.trackResetOnLoad){
9408 f.originalValue = f.getValue();
9412 }else{ // object hash
9415 if(typeof values[id] != 'function' && (field = this.findField(id))){
9417 if (field.setFromData &&
9419 field.displayField &&
9420 // combos' with local stores can
9421 // be queried via setValue()
9422 // to set their value..
9423 (field.store && !field.store.isLocal)
9427 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9428 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9429 field.setFromData(sd);
9431 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9433 field.setFromData(values);
9436 field.setValue(values[id]);
9440 if(this.trackResetOnLoad){
9441 field.originalValue = field.getValue();
9447 //Roo.each(this.childForms || [], function (f) {
9448 // f.setValues(values);
9455 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9456 * they are returned as an array.
9457 * @param {Boolean} asString
9460 getValues : function(asString){
9461 //if (this.childForms) {
9462 // copy values from the child forms
9463 // Roo.each(this.childForms, function (f) {
9464 // this.setValues(f.getValues());
9470 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9471 if(asString === true){
9474 return Roo.urlDecode(fs);
9478 * Returns the fields in this form as an object with key/value pairs.
9479 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9482 getFieldValues : function(with_hidden)
9484 var items = this.getItems();
9486 items.each(function(f){
9492 var v = f.getValue();
9494 if (f.inputType =='radio') {
9495 if (typeof(ret[f.getName()]) == 'undefined') {
9496 ret[f.getName()] = ''; // empty..
9499 if (!f.el.dom.checked) {
9507 if(f.xtype == 'MoneyField'){
9508 ret[f.currencyName] = f.getCurrency();
9511 // not sure if this supported any more..
9512 if ((typeof(v) == 'object') && f.getRawValue) {
9513 v = f.getRawValue() ; // dates..
9515 // combo boxes where name != hiddenName...
9516 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9517 ret[f.name] = f.getRawValue();
9519 ret[f.getName()] = v;
9526 * Clears all invalid messages in this form.
9527 * @return {BasicForm} this
9529 clearInvalid : function(){
9530 var items = this.getItems();
9532 items.each(function(f){
9541 * @return {BasicForm} this
9544 var items = this.getItems();
9545 items.each(function(f){
9549 Roo.each(this.childForms || [], function (f) {
9557 getItems : function()
9559 var r=new Roo.util.MixedCollection(false, function(o){
9560 return o.id || (o.id = Roo.id());
9562 var iter = function(el) {
9569 Roo.each(el.items,function(e) {
9578 hideFields : function(items)
9580 Roo.each(items, function(i){
9582 var f = this.findField(i);
9593 showFields : function(items)
9595 Roo.each(items, function(i){
9597 var f = this.findField(i);
9610 Roo.apply(Roo.bootstrap.Form, {
9637 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9638 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9639 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9640 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9643 this.maskEl.top.enableDisplayMode("block");
9644 this.maskEl.left.enableDisplayMode("block");
9645 this.maskEl.bottom.enableDisplayMode("block");
9646 this.maskEl.right.enableDisplayMode("block");
9648 this.toolTip = new Roo.bootstrap.Tooltip({
9649 cls : 'roo-form-error-popover',
9651 'left' : ['r-l', [-2,0], 'right'],
9652 'right' : ['l-r', [2,0], 'left'],
9653 'bottom' : ['tl-bl', [0,2], 'top'],
9654 'top' : [ 'bl-tl', [0,-2], 'bottom']
9658 this.toolTip.render(Roo.get(document.body));
9660 this.toolTip.el.enableDisplayMode("block");
9662 Roo.get(document.body).on('click', function(){
9666 Roo.get(document.body).on('touchstart', function(){
9670 this.isApplied = true
9673 mask : function(form, target)
9677 this.target = target;
9679 if(!this.form.errorMask || !target.el){
9683 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9685 Roo.log(scrollable);
9687 var ot = this.target.el.calcOffsetsTo(scrollable);
9689 var scrollTo = ot[1] - this.form.maskOffset;
9691 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9693 scrollable.scrollTo('top', scrollTo);
9695 var box = this.target.el.getBox();
9697 var zIndex = Roo.bootstrap.Modal.zIndex++;
9700 this.maskEl.top.setStyle('position', 'absolute');
9701 this.maskEl.top.setStyle('z-index', zIndex);
9702 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9703 this.maskEl.top.setLeft(0);
9704 this.maskEl.top.setTop(0);
9705 this.maskEl.top.show();
9707 this.maskEl.left.setStyle('position', 'absolute');
9708 this.maskEl.left.setStyle('z-index', zIndex);
9709 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9710 this.maskEl.left.setLeft(0);
9711 this.maskEl.left.setTop(box.y - this.padding);
9712 this.maskEl.left.show();
9714 this.maskEl.bottom.setStyle('position', 'absolute');
9715 this.maskEl.bottom.setStyle('z-index', zIndex);
9716 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9717 this.maskEl.bottom.setLeft(0);
9718 this.maskEl.bottom.setTop(box.bottom + this.padding);
9719 this.maskEl.bottom.show();
9721 this.maskEl.right.setStyle('position', 'absolute');
9722 this.maskEl.right.setStyle('z-index', zIndex);
9723 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9724 this.maskEl.right.setLeft(box.right + this.padding);
9725 this.maskEl.right.setTop(box.y - this.padding);
9726 this.maskEl.right.show();
9728 this.toolTip.bindEl = this.target.el;
9730 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9732 var tip = this.target.blankText;
9734 if(this.target.getValue() !== '' ) {
9736 if (this.target.invalidText.length) {
9737 tip = this.target.invalidText;
9738 } else if (this.target.regexText.length){
9739 tip = this.target.regexText;
9743 this.toolTip.show(tip);
9745 this.intervalID = window.setInterval(function() {
9746 Roo.bootstrap.Form.popover.unmask();
9749 window.onwheel = function(){ return false;};
9751 (function(){ this.isMasked = true; }).defer(500, this);
9757 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9761 this.maskEl.top.setStyle('position', 'absolute');
9762 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9763 this.maskEl.top.hide();
9765 this.maskEl.left.setStyle('position', 'absolute');
9766 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9767 this.maskEl.left.hide();
9769 this.maskEl.bottom.setStyle('position', 'absolute');
9770 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9771 this.maskEl.bottom.hide();
9773 this.maskEl.right.setStyle('position', 'absolute');
9774 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9775 this.maskEl.right.hide();
9777 this.toolTip.hide();
9779 this.toolTip.el.hide();
9781 window.onwheel = function(){ return true;};
9783 if(this.intervalID){
9784 window.clearInterval(this.intervalID);
9785 this.intervalID = false;
9788 this.isMasked = false;
9798 * Ext JS Library 1.1.1
9799 * Copyright(c) 2006-2007, Ext JS, LLC.
9801 * Originally Released Under LGPL - original licence link has changed is not relivant.
9804 * <script type="text/javascript">
9807 * @class Roo.form.VTypes
9808 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9811 Roo.form.VTypes = function(){
9812 // closure these in so they are only created once.
9813 var alpha = /^[a-zA-Z_]+$/;
9814 var alphanum = /^[a-zA-Z0-9_]+$/;
9815 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9816 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9818 // All these messages and functions are configurable
9821 * The function used to validate email addresses
9822 * @param {String} value The email address
9824 'email' : function(v){
9825 return email.test(v);
9828 * The error text to display when the email validation function returns false
9831 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9833 * The keystroke filter mask to be applied on email input
9836 'emailMask' : /[a-z0-9_\.\-@]/i,
9839 * The function used to validate URLs
9840 * @param {String} value The URL
9842 'url' : function(v){
9846 * The error text to display when the url validation function returns false
9849 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9852 * The function used to validate alpha values
9853 * @param {String} value The value
9855 'alpha' : function(v){
9856 return alpha.test(v);
9859 * The error text to display when the alpha validation function returns false
9862 'alphaText' : 'This field should only contain letters and _',
9864 * The keystroke filter mask to be applied on alpha input
9867 'alphaMask' : /[a-z_]/i,
9870 * The function used to validate alphanumeric values
9871 * @param {String} value The value
9873 'alphanum' : function(v){
9874 return alphanum.test(v);
9877 * The error text to display when the alphanumeric validation function returns false
9880 'alphanumText' : 'This field should only contain letters, numbers and _',
9882 * The keystroke filter mask to be applied on alphanumeric input
9885 'alphanumMask' : /[a-z0-9_]/i
9895 * @class Roo.bootstrap.Input
9896 * @extends Roo.bootstrap.Component
9897 * Bootstrap Input class
9898 * @cfg {Boolean} disabled is it disabled
9899 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9900 * @cfg {String} name name of the input
9901 * @cfg {string} fieldLabel - the label associated
9902 * @cfg {string} placeholder - placeholder to put in text.
9903 * @cfg {string} before - input group add on before
9904 * @cfg {string} after - input group add on after
9905 * @cfg {string} size - (lg|sm) or leave empty..
9906 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9907 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9908 * @cfg {Number} md colspan out of 12 for computer-sized screens
9909 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9910 * @cfg {string} value default value of the input
9911 * @cfg {Number} labelWidth set the width of label
9912 * @cfg {Number} labellg set the width of label (1-12)
9913 * @cfg {Number} labelmd set the width of label (1-12)
9914 * @cfg {Number} labelsm set the width of label (1-12)
9915 * @cfg {Number} labelxs set the width of label (1-12)
9916 * @cfg {String} labelAlign (top|left)
9917 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9918 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9919 * @cfg {String} indicatorpos (left|right) default left
9920 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9921 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9923 * @cfg {String} align (left|center|right) Default left
9924 * @cfg {Boolean} forceFeedback (true|false) Default false
9927 * Create a new Input
9928 * @param {Object} config The config object
9931 Roo.bootstrap.Input = function(config){
9933 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9938 * Fires when this field receives input focus.
9939 * @param {Roo.form.Field} this
9944 * Fires when this field loses input focus.
9945 * @param {Roo.form.Field} this
9950 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9951 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9952 * @param {Roo.form.Field} this
9953 * @param {Roo.EventObject} e The event object
9958 * Fires just before the field blurs if the field value has changed.
9959 * @param {Roo.form.Field} this
9960 * @param {Mixed} newValue The new value
9961 * @param {Mixed} oldValue The original value
9966 * Fires after the field has been marked as invalid.
9967 * @param {Roo.form.Field} this
9968 * @param {String} msg The validation message
9973 * Fires after the field has been validated with no errors.
9974 * @param {Roo.form.Field} this
9979 * Fires after the key up
9980 * @param {Roo.form.Field} this
9981 * @param {Roo.EventObject} e The event Object
9987 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9989 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9990 automatic validation (defaults to "keyup").
9992 validationEvent : "keyup",
9994 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9996 validateOnBlur : true,
9998 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10000 validationDelay : 250,
10002 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10004 focusClass : "x-form-focus", // not needed???
10008 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10010 invalidClass : "has-warning",
10013 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10015 validClass : "has-success",
10018 * @cfg {Boolean} hasFeedback (true|false) default true
10020 hasFeedback : true,
10023 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10025 invalidFeedbackClass : "glyphicon-warning-sign",
10028 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10030 validFeedbackClass : "glyphicon-ok",
10033 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10035 selectOnFocus : false,
10038 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10042 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10047 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10049 disableKeyFilter : false,
10052 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10056 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10060 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10062 blankText : "Please complete this mandatory field",
10065 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10069 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10071 maxLength : Number.MAX_VALUE,
10073 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10075 minLengthText : "The minimum length for this field is {0}",
10077 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10079 maxLengthText : "The maximum length for this field is {0}",
10083 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10084 * If available, this function will be called only after the basic validators all return true, and will be passed the
10085 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10089 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10090 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10091 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10095 * @cfg {String} regexText -- Depricated - use Invalid Text
10100 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10106 autocomplete: false,
10110 inputType : 'text',
10113 placeholder: false,
10118 preventMark: false,
10119 isFormField : true,
10122 labelAlign : false,
10125 formatedValue : false,
10126 forceFeedback : false,
10128 indicatorpos : 'left',
10138 parentLabelAlign : function()
10141 while (parent.parent()) {
10142 parent = parent.parent();
10143 if (typeof(parent.labelAlign) !='undefined') {
10144 return parent.labelAlign;
10151 getAutoCreate : function()
10153 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10159 if(this.inputType != 'hidden'){
10160 cfg.cls = 'form-group' //input-group
10166 type : this.inputType,
10167 value : this.value,
10168 cls : 'form-control',
10169 placeholder : this.placeholder || '',
10170 autocomplete : this.autocomplete || 'new-password'
10173 if(this.capture.length){
10174 input.capture = this.capture;
10177 if(this.accept.length){
10178 input.accept = this.accept + "/*";
10182 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10185 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10186 input.maxLength = this.maxLength;
10189 if (this.disabled) {
10190 input.disabled=true;
10193 if (this.readOnly) {
10194 input.readonly=true;
10198 input.name = this.name;
10202 input.cls += ' input-' + this.size;
10206 ['xs','sm','md','lg'].map(function(size){
10207 if (settings[size]) {
10208 cfg.cls += ' col-' + size + '-' + settings[size];
10212 var inputblock = input;
10216 cls: 'glyphicon form-control-feedback'
10219 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10222 cls : 'has-feedback',
10230 if (this.before || this.after) {
10233 cls : 'input-group',
10237 if (this.before && typeof(this.before) == 'string') {
10239 inputblock.cn.push({
10241 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10245 if (this.before && typeof(this.before) == 'object') {
10246 this.before = Roo.factory(this.before);
10248 inputblock.cn.push({
10250 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10251 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10255 inputblock.cn.push(input);
10257 if (this.after && typeof(this.after) == 'string') {
10258 inputblock.cn.push({
10260 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10264 if (this.after && typeof(this.after) == 'object') {
10265 this.after = Roo.factory(this.after);
10267 inputblock.cn.push({
10269 cls : 'roo-input-after input-group-append input-group-text input-group-' +
10270 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10274 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10275 inputblock.cls += ' has-feedback';
10276 inputblock.cn.push(feedback);
10281 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10282 tooltip : 'This field is required'
10284 if (Roo.bootstrap.version == 4) {
10287 style : 'display-none'
10290 if (align ==='left' && this.fieldLabel.length) {
10292 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10299 cls : 'control-label col-form-label',
10300 html : this.fieldLabel
10311 var labelCfg = cfg.cn[1];
10312 var contentCfg = cfg.cn[2];
10314 if(this.indicatorpos == 'right'){
10319 cls : 'control-label col-form-label',
10323 html : this.fieldLabel
10337 labelCfg = cfg.cn[0];
10338 contentCfg = cfg.cn[1];
10342 if(this.labelWidth > 12){
10343 labelCfg.style = "width: " + this.labelWidth + 'px';
10346 if(this.labelWidth < 13 && this.labelmd == 0){
10347 this.labelmd = this.labelWidth;
10350 if(this.labellg > 0){
10351 labelCfg.cls += ' col-lg-' + this.labellg;
10352 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10355 if(this.labelmd > 0){
10356 labelCfg.cls += ' col-md-' + this.labelmd;
10357 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10360 if(this.labelsm > 0){
10361 labelCfg.cls += ' col-sm-' + this.labelsm;
10362 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10365 if(this.labelxs > 0){
10366 labelCfg.cls += ' col-xs-' + this.labelxs;
10367 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10371 } else if ( this.fieldLabel.length) {
10376 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10377 tooltip : 'This field is required'
10381 //cls : 'input-group-addon',
10382 html : this.fieldLabel
10390 if(this.indicatorpos == 'right'){
10395 //cls : 'input-group-addon',
10396 html : this.fieldLabel
10401 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10402 tooltip : 'This field is required'
10422 if (this.parentType === 'Navbar' && this.parent().bar) {
10423 cfg.cls += ' navbar-form';
10426 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10427 // on BS4 we do this only if not form
10428 cfg.cls += ' navbar-form';
10436 * return the real input element.
10438 inputEl: function ()
10440 return this.el.select('input.form-control',true).first();
10443 tooltipEl : function()
10445 return this.inputEl();
10448 indicatorEl : function()
10450 if (Roo.bootstrap.version == 4) {
10451 return false; // not enabled in v4 yet.
10454 var indicator = this.el.select('i.roo-required-indicator',true).first();
10464 setDisabled : function(v)
10466 var i = this.inputEl().dom;
10468 i.removeAttribute('disabled');
10472 i.setAttribute('disabled','true');
10474 initEvents : function()
10477 this.inputEl().on("keydown" , this.fireKey, this);
10478 this.inputEl().on("focus", this.onFocus, this);
10479 this.inputEl().on("blur", this.onBlur, this);
10481 this.inputEl().relayEvent('keyup', this);
10483 this.indicator = this.indicatorEl();
10485 if(this.indicator){
10486 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10489 // reference to original value for reset
10490 this.originalValue = this.getValue();
10491 //Roo.form.TextField.superclass.initEvents.call(this);
10492 if(this.validationEvent == 'keyup'){
10493 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10494 this.inputEl().on('keyup', this.filterValidation, this);
10496 else if(this.validationEvent !== false){
10497 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10500 if(this.selectOnFocus){
10501 this.on("focus", this.preFocus, this);
10504 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10505 this.inputEl().on("keypress", this.filterKeys, this);
10507 this.inputEl().relayEvent('keypress', this);
10510 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10511 this.el.on("click", this.autoSize, this);
10514 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10515 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10518 if (typeof(this.before) == 'object') {
10519 this.before.render(this.el.select('.roo-input-before',true).first());
10521 if (typeof(this.after) == 'object') {
10522 this.after.render(this.el.select('.roo-input-after',true).first());
10525 this.inputEl().on('change', this.onChange, this);
10528 filterValidation : function(e){
10529 if(!e.isNavKeyPress()){
10530 this.validationTask.delay(this.validationDelay);
10534 * Validates the field value
10535 * @return {Boolean} True if the value is valid, else false
10537 validate : function(){
10538 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10539 if(this.disabled || this.validateValue(this.getRawValue())){
10544 this.markInvalid();
10550 * Validates a value according to the field's validation rules and marks the field as invalid
10551 * if the validation fails
10552 * @param {Mixed} value The value to validate
10553 * @return {Boolean} True if the value is valid, else false
10555 validateValue : function(value)
10557 if(this.getVisibilityEl().hasClass('hidden')){
10561 if(value.length < 1) { // if it's blank
10562 if(this.allowBlank){
10568 if(value.length < this.minLength){
10571 if(value.length > this.maxLength){
10575 var vt = Roo.form.VTypes;
10576 if(!vt[this.vtype](value, this)){
10580 if(typeof this.validator == "function"){
10581 var msg = this.validator(value);
10585 if (typeof(msg) == 'string') {
10586 this.invalidText = msg;
10590 if(this.regex && !this.regex.test(value)){
10598 fireKey : function(e){
10599 //Roo.log('field ' + e.getKey());
10600 if(e.isNavKeyPress()){
10601 this.fireEvent("specialkey", this, e);
10604 focus : function (selectText){
10606 this.inputEl().focus();
10607 if(selectText === true){
10608 this.inputEl().dom.select();
10614 onFocus : function(){
10615 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10616 // this.el.addClass(this.focusClass);
10618 if(!this.hasFocus){
10619 this.hasFocus = true;
10620 this.startValue = this.getValue();
10621 this.fireEvent("focus", this);
10625 beforeBlur : Roo.emptyFn,
10629 onBlur : function(){
10631 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10632 //this.el.removeClass(this.focusClass);
10634 this.hasFocus = false;
10635 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10638 var v = this.getValue();
10639 if(String(v) !== String(this.startValue)){
10640 this.fireEvent('change', this, v, this.startValue);
10642 this.fireEvent("blur", this);
10645 onChange : function(e)
10647 var v = this.getValue();
10648 if(String(v) !== String(this.startValue)){
10649 this.fireEvent('change', this, v, this.startValue);
10655 * Resets the current field value to the originally loaded value and clears any validation messages
10657 reset : function(){
10658 this.setValue(this.originalValue);
10662 * Returns the name of the field
10663 * @return {Mixed} name The name field
10665 getName: function(){
10669 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10670 * @return {Mixed} value The field value
10672 getValue : function(){
10674 var v = this.inputEl().getValue();
10679 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10680 * @return {Mixed} value The field value
10682 getRawValue : function(){
10683 var v = this.inputEl().getValue();
10689 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10690 * @param {Mixed} value The value to set
10692 setRawValue : function(v){
10693 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10696 selectText : function(start, end){
10697 var v = this.getRawValue();
10699 start = start === undefined ? 0 : start;
10700 end = end === undefined ? v.length : end;
10701 var d = this.inputEl().dom;
10702 if(d.setSelectionRange){
10703 d.setSelectionRange(start, end);
10704 }else if(d.createTextRange){
10705 var range = d.createTextRange();
10706 range.moveStart("character", start);
10707 range.moveEnd("character", v.length-end);
10714 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10715 * @param {Mixed} value The value to set
10717 setValue : function(v){
10720 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10726 processValue : function(value){
10727 if(this.stripCharsRe){
10728 var newValue = value.replace(this.stripCharsRe, '');
10729 if(newValue !== value){
10730 this.setRawValue(newValue);
10737 preFocus : function(){
10739 if(this.selectOnFocus){
10740 this.inputEl().dom.select();
10743 filterKeys : function(e){
10744 var k = e.getKey();
10745 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10748 var c = e.getCharCode(), cc = String.fromCharCode(c);
10749 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10752 if(!this.maskRe.test(cc)){
10757 * Clear any invalid styles/messages for this field
10759 clearInvalid : function(){
10761 if(!this.el || this.preventMark){ // not rendered
10766 this.el.removeClass([this.invalidClass, 'is-invalid']);
10768 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10770 var feedback = this.el.select('.form-control-feedback', true).first();
10773 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10778 if(this.indicator){
10779 this.indicator.removeClass('visible');
10780 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10783 this.fireEvent('valid', this);
10787 * Mark this field as valid
10789 markValid : function()
10791 if(!this.el || this.preventMark){ // not rendered...
10795 this.el.removeClass([this.invalidClass, this.validClass]);
10796 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10798 var feedback = this.el.select('.form-control-feedback', true).first();
10801 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10804 if(this.indicator){
10805 this.indicator.removeClass('visible');
10806 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10813 if(this.allowBlank && !this.getRawValue().length){
10816 if (Roo.bootstrap.version == 3) {
10817 this.el.addClass(this.validClass);
10819 this.inputEl().addClass('is-valid');
10822 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10824 var feedback = this.el.select('.form-control-feedback', true).first();
10827 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10828 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10833 this.fireEvent('valid', this);
10837 * Mark this field as invalid
10838 * @param {String} msg The validation message
10840 markInvalid : function(msg)
10842 if(!this.el || this.preventMark){ // not rendered
10846 this.el.removeClass([this.invalidClass, this.validClass]);
10847 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10849 var feedback = this.el.select('.form-control-feedback', true).first();
10852 this.el.select('.form-control-feedback', true).first().removeClass(
10853 [this.invalidFeedbackClass, this.validFeedbackClass]);
10860 if(this.allowBlank && !this.getRawValue().length){
10864 if(this.indicator){
10865 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10866 this.indicator.addClass('visible');
10868 if (Roo.bootstrap.version == 3) {
10869 this.el.addClass(this.invalidClass);
10871 this.inputEl().addClass('is-invalid');
10876 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10878 var feedback = this.el.select('.form-control-feedback', true).first();
10881 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10883 if(this.getValue().length || this.forceFeedback){
10884 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10891 this.fireEvent('invalid', this, msg);
10894 SafariOnKeyDown : function(event)
10896 // this is a workaround for a password hang bug on chrome/ webkit.
10897 if (this.inputEl().dom.type != 'password') {
10901 var isSelectAll = false;
10903 if(this.inputEl().dom.selectionEnd > 0){
10904 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10906 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10907 event.preventDefault();
10912 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10914 event.preventDefault();
10915 // this is very hacky as keydown always get's upper case.
10917 var cc = String.fromCharCode(event.getCharCode());
10918 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10922 adjustWidth : function(tag, w){
10923 tag = tag.toLowerCase();
10924 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10925 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10926 if(tag == 'input'){
10929 if(tag == 'textarea'){
10932 }else if(Roo.isOpera){
10933 if(tag == 'input'){
10936 if(tag == 'textarea'){
10944 setFieldLabel : function(v)
10946 if(!this.rendered){
10950 if(this.indicatorEl()){
10951 var ar = this.el.select('label > span',true);
10953 if (ar.elements.length) {
10954 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10955 this.fieldLabel = v;
10959 var br = this.el.select('label',true);
10961 if(br.elements.length) {
10962 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10963 this.fieldLabel = v;
10967 Roo.log('Cannot Found any of label > span || label in input');
10971 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10972 this.fieldLabel = v;
10987 * @class Roo.bootstrap.TextArea
10988 * @extends Roo.bootstrap.Input
10989 * Bootstrap TextArea class
10990 * @cfg {Number} cols Specifies the visible width of a text area
10991 * @cfg {Number} rows Specifies the visible number of lines in a text area
10992 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10993 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10994 * @cfg {string} html text
10997 * Create a new TextArea
10998 * @param {Object} config The config object
11001 Roo.bootstrap.TextArea = function(config){
11002 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11006 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11016 getAutoCreate : function(){
11018 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11024 if(this.inputType != 'hidden'){
11025 cfg.cls = 'form-group' //input-group
11033 value : this.value || '',
11034 html: this.html || '',
11035 cls : 'form-control',
11036 placeholder : this.placeholder || ''
11040 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11041 input.maxLength = this.maxLength;
11045 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11049 input.cols = this.cols;
11052 if (this.readOnly) {
11053 input.readonly = true;
11057 input.name = this.name;
11061 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11065 ['xs','sm','md','lg'].map(function(size){
11066 if (settings[size]) {
11067 cfg.cls += ' col-' + size + '-' + settings[size];
11071 var inputblock = input;
11073 if(this.hasFeedback && !this.allowBlank){
11077 cls: 'glyphicon form-control-feedback'
11081 cls : 'has-feedback',
11090 if (this.before || this.after) {
11093 cls : 'input-group',
11097 inputblock.cn.push({
11099 cls : 'input-group-addon',
11104 inputblock.cn.push(input);
11106 if(this.hasFeedback && !this.allowBlank){
11107 inputblock.cls += ' has-feedback';
11108 inputblock.cn.push(feedback);
11112 inputblock.cn.push({
11114 cls : 'input-group-addon',
11121 if (align ==='left' && this.fieldLabel.length) {
11126 cls : 'control-label',
11127 html : this.fieldLabel
11138 if(this.labelWidth > 12){
11139 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11142 if(this.labelWidth < 13 && this.labelmd == 0){
11143 this.labelmd = this.labelWidth;
11146 if(this.labellg > 0){
11147 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11148 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11151 if(this.labelmd > 0){
11152 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11153 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11156 if(this.labelsm > 0){
11157 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11158 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11161 if(this.labelxs > 0){
11162 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11163 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11166 } else if ( this.fieldLabel.length) {
11171 //cls : 'input-group-addon',
11172 html : this.fieldLabel
11190 if (this.disabled) {
11191 input.disabled=true;
11198 * return the real textarea element.
11200 inputEl: function ()
11202 return this.el.select('textarea.form-control',true).first();
11206 * Clear any invalid styles/messages for this field
11208 clearInvalid : function()
11211 if(!this.el || this.preventMark){ // not rendered
11215 var label = this.el.select('label', true).first();
11216 var icon = this.el.select('i.fa-star', true).first();
11221 this.el.removeClass( this.validClass);
11222 this.inputEl().removeClass('is-invalid');
11224 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11226 var feedback = this.el.select('.form-control-feedback', true).first();
11229 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11234 this.fireEvent('valid', this);
11238 * Mark this field as valid
11240 markValid : function()
11242 if(!this.el || this.preventMark){ // not rendered
11246 this.el.removeClass([this.invalidClass, this.validClass]);
11247 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11249 var feedback = this.el.select('.form-control-feedback', true).first();
11252 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11255 if(this.disabled || this.allowBlank){
11259 var label = this.el.select('label', true).first();
11260 var icon = this.el.select('i.fa-star', true).first();
11265 if (Roo.bootstrap.version == 3) {
11266 this.el.addClass(this.validClass);
11268 this.inputEl().addClass('is-valid');
11272 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11274 var feedback = this.el.select('.form-control-feedback', true).first();
11277 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11278 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11283 this.fireEvent('valid', this);
11287 * Mark this field as invalid
11288 * @param {String} msg The validation message
11290 markInvalid : function(msg)
11292 if(!this.el || this.preventMark){ // not rendered
11296 this.el.removeClass([this.invalidClass, this.validClass]);
11297 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11299 var feedback = this.el.select('.form-control-feedback', true).first();
11302 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11305 if(this.disabled || this.allowBlank){
11309 var label = this.el.select('label', true).first();
11310 var icon = this.el.select('i.fa-star', true).first();
11312 if(!this.getValue().length && label && !icon){
11313 this.el.createChild({
11315 cls : 'text-danger fa fa-lg fa-star',
11316 tooltip : 'This field is required',
11317 style : 'margin-right:5px;'
11321 if (Roo.bootstrap.version == 3) {
11322 this.el.addClass(this.invalidClass);
11324 this.inputEl().addClass('is-invalid');
11327 // fixme ... this may be depricated need to test..
11328 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11330 var feedback = this.el.select('.form-control-feedback', true).first();
11333 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11335 if(this.getValue().length || this.forceFeedback){
11336 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11343 this.fireEvent('invalid', this, msg);
11351 * trigger field - base class for combo..
11356 * @class Roo.bootstrap.TriggerField
11357 * @extends Roo.bootstrap.Input
11358 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11359 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11360 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11361 * for which you can provide a custom implementation. For example:
11363 var trigger = new Roo.bootstrap.TriggerField();
11364 trigger.onTriggerClick = myTriggerFn;
11365 trigger.applyTo('my-field');
11368 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11369 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11370 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11371 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11372 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11375 * Create a new TriggerField.
11376 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11377 * to the base TextField)
11379 Roo.bootstrap.TriggerField = function(config){
11380 this.mimicing = false;
11381 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11384 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11386 * @cfg {String} triggerClass A CSS class to apply to the trigger
11389 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11394 * @cfg {Boolean} removable (true|false) special filter default false
11398 /** @cfg {Boolean} grow @hide */
11399 /** @cfg {Number} growMin @hide */
11400 /** @cfg {Number} growMax @hide */
11406 autoSize: Roo.emptyFn,
11410 deferHeight : true,
11413 actionMode : 'wrap',
11418 getAutoCreate : function(){
11420 var align = this.labelAlign || this.parentLabelAlign();
11425 cls: 'form-group' //input-group
11432 type : this.inputType,
11433 cls : 'form-control',
11434 autocomplete: 'new-password',
11435 placeholder : this.placeholder || ''
11439 input.name = this.name;
11442 input.cls += ' input-' + this.size;
11445 if (this.disabled) {
11446 input.disabled=true;
11449 var inputblock = input;
11451 if(this.hasFeedback && !this.allowBlank){
11455 cls: 'glyphicon form-control-feedback'
11458 if(this.removable && !this.editable && !this.tickable){
11460 cls : 'has-feedback',
11466 cls : 'roo-combo-removable-btn close'
11473 cls : 'has-feedback',
11482 if(this.removable && !this.editable && !this.tickable){
11484 cls : 'roo-removable',
11490 cls : 'roo-combo-removable-btn close'
11497 if (this.before || this.after) {
11500 cls : 'input-group',
11504 inputblock.cn.push({
11506 cls : 'input-group-addon input-group-prepend input-group-text',
11511 inputblock.cn.push(input);
11513 if(this.hasFeedback && !this.allowBlank){
11514 inputblock.cls += ' has-feedback';
11515 inputblock.cn.push(feedback);
11519 inputblock.cn.push({
11521 cls : 'input-group-addon input-group-append input-group-text',
11530 var ibwrap = inputblock;
11535 cls: 'roo-select2-choices',
11539 cls: 'roo-select2-search-field',
11551 cls: 'roo-select2-container input-group',
11556 cls: 'form-hidden-field'
11562 if(!this.multiple && this.showToggleBtn){
11568 if (this.caret != false) {
11571 cls: 'fa fa-' + this.caret
11578 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11580 Roo.bootstrap.version == 3 ? caret : '',
11583 cls: 'combobox-clear',
11597 combobox.cls += ' roo-select2-container-multi';
11601 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11602 tooltip : 'This field is required'
11604 if (Roo.bootstrap.version == 4) {
11607 style : 'display:none'
11612 if (align ==='left' && this.fieldLabel.length) {
11614 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11621 cls : 'control-label',
11622 html : this.fieldLabel
11634 var labelCfg = cfg.cn[1];
11635 var contentCfg = cfg.cn[2];
11637 if(this.indicatorpos == 'right'){
11642 cls : 'control-label',
11646 html : this.fieldLabel
11660 labelCfg = cfg.cn[0];
11661 contentCfg = cfg.cn[1];
11664 if(this.labelWidth > 12){
11665 labelCfg.style = "width: " + this.labelWidth + 'px';
11668 if(this.labelWidth < 13 && this.labelmd == 0){
11669 this.labelmd = this.labelWidth;
11672 if(this.labellg > 0){
11673 labelCfg.cls += ' col-lg-' + this.labellg;
11674 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11677 if(this.labelmd > 0){
11678 labelCfg.cls += ' col-md-' + this.labelmd;
11679 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11682 if(this.labelsm > 0){
11683 labelCfg.cls += ' col-sm-' + this.labelsm;
11684 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11687 if(this.labelxs > 0){
11688 labelCfg.cls += ' col-xs-' + this.labelxs;
11689 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11692 } else if ( this.fieldLabel.length) {
11693 // Roo.log(" label");
11698 //cls : 'input-group-addon',
11699 html : this.fieldLabel
11707 if(this.indicatorpos == 'right'){
11715 html : this.fieldLabel
11729 // Roo.log(" no label && no align");
11736 ['xs','sm','md','lg'].map(function(size){
11737 if (settings[size]) {
11738 cfg.cls += ' col-' + size + '-' + settings[size];
11749 onResize : function(w, h){
11750 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11751 // if(typeof w == 'number'){
11752 // var x = w - this.trigger.getWidth();
11753 // this.inputEl().setWidth(this.adjustWidth('input', x));
11754 // this.trigger.setStyle('left', x+'px');
11759 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11762 getResizeEl : function(){
11763 return this.inputEl();
11767 getPositionEl : function(){
11768 return this.inputEl();
11772 alignErrorIcon : function(){
11773 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11777 initEvents : function(){
11781 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11782 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11783 if(!this.multiple && this.showToggleBtn){
11784 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11785 if(this.hideTrigger){
11786 this.trigger.setDisplayed(false);
11788 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11792 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11795 if(this.removable && !this.editable && !this.tickable){
11796 var close = this.closeTriggerEl();
11799 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11800 close.on('click', this.removeBtnClick, this, close);
11804 //this.trigger.addClassOnOver('x-form-trigger-over');
11805 //this.trigger.addClassOnClick('x-form-trigger-click');
11808 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11812 closeTriggerEl : function()
11814 var close = this.el.select('.roo-combo-removable-btn', true).first();
11815 return close ? close : false;
11818 removeBtnClick : function(e, h, el)
11820 e.preventDefault();
11822 if(this.fireEvent("remove", this) !== false){
11824 this.fireEvent("afterremove", this)
11828 createList : function()
11830 this.list = Roo.get(document.body).createChild({
11831 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11832 cls: 'typeahead typeahead-long dropdown-menu',
11833 style: 'display:none'
11836 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11841 initTrigger : function(){
11846 onDestroy : function(){
11848 this.trigger.removeAllListeners();
11849 // this.trigger.remove();
11852 // this.wrap.remove();
11854 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11858 onFocus : function(){
11859 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11861 if(!this.mimicing){
11862 this.wrap.addClass('x-trigger-wrap-focus');
11863 this.mimicing = true;
11864 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11865 if(this.monitorTab){
11866 this.el.on("keydown", this.checkTab, this);
11873 checkTab : function(e){
11874 if(e.getKey() == e.TAB){
11875 this.triggerBlur();
11880 onBlur : function(){
11885 mimicBlur : function(e, t){
11887 if(!this.wrap.contains(t) && this.validateBlur()){
11888 this.triggerBlur();
11894 triggerBlur : function(){
11895 this.mimicing = false;
11896 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11897 if(this.monitorTab){
11898 this.el.un("keydown", this.checkTab, this);
11900 //this.wrap.removeClass('x-trigger-wrap-focus');
11901 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11905 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11906 validateBlur : function(e, t){
11911 onDisable : function(){
11912 this.inputEl().dom.disabled = true;
11913 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11915 // this.wrap.addClass('x-item-disabled');
11920 onEnable : function(){
11921 this.inputEl().dom.disabled = false;
11922 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11924 // this.el.removeClass('x-item-disabled');
11929 onShow : function(){
11930 var ae = this.getActionEl();
11933 ae.dom.style.display = '';
11934 ae.dom.style.visibility = 'visible';
11940 onHide : function(){
11941 var ae = this.getActionEl();
11942 ae.dom.style.display = 'none';
11946 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11947 * by an implementing function.
11949 * @param {EventObject} e
11951 onTriggerClick : Roo.emptyFn
11955 * Ext JS Library 1.1.1
11956 * Copyright(c) 2006-2007, Ext JS, LLC.
11958 * Originally Released Under LGPL - original licence link has changed is not relivant.
11961 * <script type="text/javascript">
11966 * @class Roo.data.SortTypes
11968 * Defines the default sorting (casting?) comparison functions used when sorting data.
11970 Roo.data.SortTypes = {
11972 * Default sort that does nothing
11973 * @param {Mixed} s The value being converted
11974 * @return {Mixed} The comparison value
11976 none : function(s){
11981 * The regular expression used to strip tags
11985 stripTagsRE : /<\/?[^>]+>/gi,
11988 * Strips all HTML tags to sort on text only
11989 * @param {Mixed} s The value being converted
11990 * @return {String} The comparison value
11992 asText : function(s){
11993 return String(s).replace(this.stripTagsRE, "");
11997 * Strips all HTML tags to sort on text only - Case insensitive
11998 * @param {Mixed} s The value being converted
11999 * @return {String} The comparison value
12001 asUCText : function(s){
12002 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12006 * Case insensitive string
12007 * @param {Mixed} s The value being converted
12008 * @return {String} The comparison value
12010 asUCString : function(s) {
12011 return String(s).toUpperCase();
12016 * @param {Mixed} s The value being converted
12017 * @return {Number} The comparison value
12019 asDate : function(s) {
12023 if(s instanceof Date){
12024 return s.getTime();
12026 return Date.parse(String(s));
12031 * @param {Mixed} s The value being converted
12032 * @return {Float} The comparison value
12034 asFloat : function(s) {
12035 var val = parseFloat(String(s).replace(/,/g, ""));
12044 * @param {Mixed} s The value being converted
12045 * @return {Number} The comparison value
12047 asInt : function(s) {
12048 var val = parseInt(String(s).replace(/,/g, ""));
12056 * Ext JS Library 1.1.1
12057 * Copyright(c) 2006-2007, Ext JS, LLC.
12059 * Originally Released Under LGPL - original licence link has changed is not relivant.
12062 * <script type="text/javascript">
12066 * @class Roo.data.Record
12067 * Instances of this class encapsulate both record <em>definition</em> information, and record
12068 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12069 * to access Records cached in an {@link Roo.data.Store} object.<br>
12071 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12072 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12075 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12077 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12078 * {@link #create}. The parameters are the same.
12079 * @param {Array} data An associative Array of data values keyed by the field name.
12080 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12081 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12082 * not specified an integer id is generated.
12084 Roo.data.Record = function(data, id){
12085 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12090 * Generate a constructor for a specific record layout.
12091 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12092 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12093 * Each field definition object may contain the following properties: <ul>
12094 * <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,
12095 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12096 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12097 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12098 * is being used, then this is a string containing the javascript expression to reference the data relative to
12099 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12100 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12101 * this may be omitted.</p></li>
12102 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12103 * <ul><li>auto (Default, implies no conversion)</li>
12108 * <li>date</li></ul></p></li>
12109 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12110 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12111 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12112 * by the Reader into an object that will be stored in the Record. It is passed the
12113 * following parameters:<ul>
12114 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12116 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12118 * <br>usage:<br><pre><code>
12119 var TopicRecord = Roo.data.Record.create(
12120 {name: 'title', mapping: 'topic_title'},
12121 {name: 'author', mapping: 'username'},
12122 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12123 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12124 {name: 'lastPoster', mapping: 'user2'},
12125 {name: 'excerpt', mapping: 'post_text'}
12128 var myNewRecord = new TopicRecord({
12129 title: 'Do my job please',
12132 lastPost: new Date(),
12133 lastPoster: 'Animal',
12134 excerpt: 'No way dude!'
12136 myStore.add(myNewRecord);
12141 Roo.data.Record.create = function(o){
12142 var f = function(){
12143 f.superclass.constructor.apply(this, arguments);
12145 Roo.extend(f, Roo.data.Record);
12146 var p = f.prototype;
12147 p.fields = new Roo.util.MixedCollection(false, function(field){
12150 for(var i = 0, len = o.length; i < len; i++){
12151 p.fields.add(new Roo.data.Field(o[i]));
12153 f.getField = function(name){
12154 return p.fields.get(name);
12159 Roo.data.Record.AUTO_ID = 1000;
12160 Roo.data.Record.EDIT = 'edit';
12161 Roo.data.Record.REJECT = 'reject';
12162 Roo.data.Record.COMMIT = 'commit';
12164 Roo.data.Record.prototype = {
12166 * Readonly flag - true if this record has been modified.
12175 join : function(store){
12176 this.store = store;
12180 * Set the named field to the specified value.
12181 * @param {String} name The name of the field to set.
12182 * @param {Object} value The value to set the field to.
12184 set : function(name, value){
12185 if(this.data[name] == value){
12189 if(!this.modified){
12190 this.modified = {};
12192 if(typeof this.modified[name] == 'undefined'){
12193 this.modified[name] = this.data[name];
12195 this.data[name] = value;
12196 if(!this.editing && this.store){
12197 this.store.afterEdit(this);
12202 * Get the value of the named field.
12203 * @param {String} name The name of the field to get the value of.
12204 * @return {Object} The value of the field.
12206 get : function(name){
12207 return this.data[name];
12211 beginEdit : function(){
12212 this.editing = true;
12213 this.modified = {};
12217 cancelEdit : function(){
12218 this.editing = false;
12219 delete this.modified;
12223 endEdit : function(){
12224 this.editing = false;
12225 if(this.dirty && this.store){
12226 this.store.afterEdit(this);
12231 * Usually called by the {@link Roo.data.Store} which owns the Record.
12232 * Rejects all changes made to the Record since either creation, or the last commit operation.
12233 * Modified fields are reverted to their original values.
12235 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12236 * of reject operations.
12238 reject : function(){
12239 var m = this.modified;
12241 if(typeof m[n] != "function"){
12242 this.data[n] = m[n];
12245 this.dirty = false;
12246 delete this.modified;
12247 this.editing = false;
12249 this.store.afterReject(this);
12254 * Usually called by the {@link Roo.data.Store} which owns the Record.
12255 * Commits all changes made to the Record since either creation, or the last commit operation.
12257 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12258 * of commit operations.
12260 commit : function(){
12261 this.dirty = false;
12262 delete this.modified;
12263 this.editing = false;
12265 this.store.afterCommit(this);
12270 hasError : function(){
12271 return this.error != null;
12275 clearError : function(){
12280 * Creates a copy of this record.
12281 * @param {String} id (optional) A new record id if you don't want to use this record's id
12284 copy : function(newId) {
12285 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12289 * Ext JS Library 1.1.1
12290 * Copyright(c) 2006-2007, Ext JS, LLC.
12292 * Originally Released Under LGPL - original licence link has changed is not relivant.
12295 * <script type="text/javascript">
12301 * @class Roo.data.Store
12302 * @extends Roo.util.Observable
12303 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12304 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12306 * 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
12307 * has no knowledge of the format of the data returned by the Proxy.<br>
12309 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12310 * instances from the data object. These records are cached and made available through accessor functions.
12312 * Creates a new Store.
12313 * @param {Object} config A config object containing the objects needed for the Store to access data,
12314 * and read the data into Records.
12316 Roo.data.Store = function(config){
12317 this.data = new Roo.util.MixedCollection(false);
12318 this.data.getKey = function(o){
12321 this.baseParams = {};
12323 this.paramNames = {
12328 "multisort" : "_multisort"
12331 if(config && config.data){
12332 this.inlineData = config.data;
12333 delete config.data;
12336 Roo.apply(this, config);
12338 if(this.reader){ // reader passed
12339 this.reader = Roo.factory(this.reader, Roo.data);
12340 this.reader.xmodule = this.xmodule || false;
12341 if(!this.recordType){
12342 this.recordType = this.reader.recordType;
12344 if(this.reader.onMetaChange){
12345 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12349 if(this.recordType){
12350 this.fields = this.recordType.prototype.fields;
12352 this.modified = [];
12356 * @event datachanged
12357 * Fires when the data cache has changed, and a widget which is using this Store
12358 * as a Record cache should refresh its view.
12359 * @param {Store} this
12361 datachanged : true,
12363 * @event metachange
12364 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12365 * @param {Store} this
12366 * @param {Object} meta The JSON metadata
12371 * Fires when Records have been added to the Store
12372 * @param {Store} this
12373 * @param {Roo.data.Record[]} records The array of Records added
12374 * @param {Number} index The index at which the record(s) were added
12379 * Fires when a Record has been removed from the Store
12380 * @param {Store} this
12381 * @param {Roo.data.Record} record The Record that was removed
12382 * @param {Number} index The index at which the record was removed
12387 * Fires when a Record has been updated
12388 * @param {Store} this
12389 * @param {Roo.data.Record} record The Record that was updated
12390 * @param {String} operation The update operation being performed. Value may be one of:
12392 Roo.data.Record.EDIT
12393 Roo.data.Record.REJECT
12394 Roo.data.Record.COMMIT
12400 * Fires when the data cache has been cleared.
12401 * @param {Store} this
12405 * @event beforeload
12406 * Fires before a request is made for a new data object. If the beforeload handler returns false
12407 * the load action will be canceled.
12408 * @param {Store} this
12409 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12413 * @event beforeloadadd
12414 * Fires after a new set of Records has been loaded.
12415 * @param {Store} this
12416 * @param {Roo.data.Record[]} records The Records that were loaded
12417 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12419 beforeloadadd : true,
12422 * Fires after a new set of Records has been loaded, before they are added to the store.
12423 * @param {Store} this
12424 * @param {Roo.data.Record[]} records The Records that were loaded
12425 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12426 * @params {Object} return from reader
12430 * @event loadexception
12431 * Fires if an exception occurs in the Proxy during loading.
12432 * Called with the signature of the Proxy's "loadexception" event.
12433 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12436 * @param {Object} return from JsonData.reader() - success, totalRecords, records
12437 * @param {Object} load options
12438 * @param {Object} jsonData from your request (normally this contains the Exception)
12440 loadexception : true
12444 this.proxy = Roo.factory(this.proxy, Roo.data);
12445 this.proxy.xmodule = this.xmodule || false;
12446 this.relayEvents(this.proxy, ["loadexception"]);
12448 this.sortToggle = {};
12449 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12451 Roo.data.Store.superclass.constructor.call(this);
12453 if(this.inlineData){
12454 this.loadData(this.inlineData);
12455 delete this.inlineData;
12459 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12461 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
12462 * without a remote query - used by combo/forms at present.
12466 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12469 * @cfg {Array} data Inline data to be loaded when the store is initialized.
12472 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12473 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12476 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12477 * on any HTTP request
12480 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12483 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12487 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12488 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12490 remoteSort : false,
12493 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12494 * loaded or when a record is removed. (defaults to false).
12496 pruneModifiedRecords : false,
12499 lastOptions : null,
12502 * Add Records to the Store and fires the add event.
12503 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12505 add : function(records){
12506 records = [].concat(records);
12507 for(var i = 0, len = records.length; i < len; i++){
12508 records[i].join(this);
12510 var index = this.data.length;
12511 this.data.addAll(records);
12512 this.fireEvent("add", this, records, index);
12516 * Remove a Record from the Store and fires the remove event.
12517 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12519 remove : function(record){
12520 var index = this.data.indexOf(record);
12521 this.data.removeAt(index);
12523 if(this.pruneModifiedRecords){
12524 this.modified.remove(record);
12526 this.fireEvent("remove", this, record, index);
12530 * Remove all Records from the Store and fires the clear event.
12532 removeAll : function(){
12534 if(this.pruneModifiedRecords){
12535 this.modified = [];
12537 this.fireEvent("clear", this);
12541 * Inserts Records to the Store at the given index and fires the add event.
12542 * @param {Number} index The start index at which to insert the passed Records.
12543 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12545 insert : function(index, records){
12546 records = [].concat(records);
12547 for(var i = 0, len = records.length; i < len; i++){
12548 this.data.insert(index, records[i]);
12549 records[i].join(this);
12551 this.fireEvent("add", this, records, index);
12555 * Get the index within the cache of the passed Record.
12556 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12557 * @return {Number} The index of the passed Record. Returns -1 if not found.
12559 indexOf : function(record){
12560 return this.data.indexOf(record);
12564 * Get the index within the cache of the Record with the passed id.
12565 * @param {String} id The id of the Record to find.
12566 * @return {Number} The index of the Record. Returns -1 if not found.
12568 indexOfId : function(id){
12569 return this.data.indexOfKey(id);
12573 * Get the Record with the specified id.
12574 * @param {String} id The id of the Record to find.
12575 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12577 getById : function(id){
12578 return this.data.key(id);
12582 * Get the Record at the specified index.
12583 * @param {Number} index The index of the Record to find.
12584 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12586 getAt : function(index){
12587 return this.data.itemAt(index);
12591 * Returns a range of Records between specified indices.
12592 * @param {Number} startIndex (optional) The starting index (defaults to 0)
12593 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12594 * @return {Roo.data.Record[]} An array of Records
12596 getRange : function(start, end){
12597 return this.data.getRange(start, end);
12601 storeOptions : function(o){
12602 o = Roo.apply({}, o);
12605 this.lastOptions = o;
12609 * Loads the Record cache from the configured Proxy using the configured Reader.
12611 * If using remote paging, then the first load call must specify the <em>start</em>
12612 * and <em>limit</em> properties in the options.params property to establish the initial
12613 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12615 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12616 * and this call will return before the new data has been loaded. Perform any post-processing
12617 * in a callback function, or in a "load" event handler.</strong>
12619 * @param {Object} options An object containing properties which control loading options:<ul>
12620 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12621 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12622 * passed the following arguments:<ul>
12623 * <li>r : Roo.data.Record[]</li>
12624 * <li>options: Options object from the load call</li>
12625 * <li>success: Boolean success indicator</li></ul></li>
12626 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12627 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12630 load : function(options){
12631 options = options || {};
12632 if(this.fireEvent("beforeload", this, options) !== false){
12633 this.storeOptions(options);
12634 var p = Roo.apply(options.params || {}, this.baseParams);
12635 // if meta was not loaded from remote source.. try requesting it.
12636 if (!this.reader.metaFromRemote) {
12637 p._requestMeta = 1;
12639 if(this.sortInfo && this.remoteSort){
12640 var pn = this.paramNames;
12641 p[pn["sort"]] = this.sortInfo.field;
12642 p[pn["dir"]] = this.sortInfo.direction;
12644 if (this.multiSort) {
12645 var pn = this.paramNames;
12646 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12649 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12654 * Reloads the Record cache from the configured Proxy using the configured Reader and
12655 * the options from the last load operation performed.
12656 * @param {Object} options (optional) An object containing properties which may override the options
12657 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12658 * the most recently used options are reused).
12660 reload : function(options){
12661 this.load(Roo.applyIf(options||{}, this.lastOptions));
12665 // Called as a callback by the Reader during a load operation.
12666 loadRecords : function(o, options, success){
12667 if(!o || success === false){
12668 if(success !== false){
12669 this.fireEvent("load", this, [], options, o);
12671 if(options.callback){
12672 options.callback.call(options.scope || this, [], options, false);
12676 // if data returned failure - throw an exception.
12677 if (o.success === false) {
12678 // show a message if no listener is registered.
12679 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12680 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12682 // loadmask wil be hooked into this..
12683 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12686 var r = o.records, t = o.totalRecords || r.length;
12688 this.fireEvent("beforeloadadd", this, r, options, o);
12690 if(!options || options.add !== true){
12691 if(this.pruneModifiedRecords){
12692 this.modified = [];
12694 for(var i = 0, len = r.length; i < len; i++){
12698 this.data = this.snapshot;
12699 delete this.snapshot;
12702 this.data.addAll(r);
12703 this.totalLength = t;
12705 this.fireEvent("datachanged", this);
12707 this.totalLength = Math.max(t, this.data.length+r.length);
12711 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12713 var e = new Roo.data.Record({});
12715 e.set(this.parent.displayField, this.parent.emptyTitle);
12716 e.set(this.parent.valueField, '');
12721 this.fireEvent("load", this, r, options, o);
12722 if(options.callback){
12723 options.callback.call(options.scope || this, r, options, true);
12729 * Loads data from a passed data block. A Reader which understands the format of the data
12730 * must have been configured in the constructor.
12731 * @param {Object} data The data block from which to read the Records. The format of the data expected
12732 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12733 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12735 loadData : function(o, append){
12736 var r = this.reader.readRecords(o);
12737 this.loadRecords(r, {add: append}, true);
12741 * using 'cn' the nested child reader read the child array into it's child stores.
12742 * @param {Object} rec The record with a 'children array
12744 loadDataFromChildren : function(rec)
12746 this.loadData(this.reader.toLoadData(rec));
12751 * Gets the number of cached records.
12753 * <em>If using paging, this may not be the total size of the dataset. If the data object
12754 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12755 * the data set size</em>
12757 getCount : function(){
12758 return this.data.length || 0;
12762 * Gets the total number of records in the dataset as returned by the server.
12764 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12765 * the dataset size</em>
12767 getTotalCount : function(){
12768 return this.totalLength || 0;
12772 * Returns the sort state of the Store as an object with two properties:
12774 field {String} The name of the field by which the Records are sorted
12775 direction {String} The sort order, "ASC" or "DESC"
12778 getSortState : function(){
12779 return this.sortInfo;
12783 applySort : function(){
12784 if(this.sortInfo && !this.remoteSort){
12785 var s = this.sortInfo, f = s.field;
12786 var st = this.fields.get(f).sortType;
12787 var fn = function(r1, r2){
12788 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12789 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12791 this.data.sort(s.direction, fn);
12792 if(this.snapshot && this.snapshot != this.data){
12793 this.snapshot.sort(s.direction, fn);
12799 * Sets the default sort column and order to be used by the next load operation.
12800 * @param {String} fieldName The name of the field to sort by.
12801 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12803 setDefaultSort : function(field, dir){
12804 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12808 * Sort the Records.
12809 * If remote sorting is used, the sort is performed on the server, and the cache is
12810 * reloaded. If local sorting is used, the cache is sorted internally.
12811 * @param {String} fieldName The name of the field to sort by.
12812 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12814 sort : function(fieldName, dir){
12815 var f = this.fields.get(fieldName);
12817 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12819 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12820 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12825 this.sortToggle[f.name] = dir;
12826 this.sortInfo = {field: f.name, direction: dir};
12827 if(!this.remoteSort){
12829 this.fireEvent("datachanged", this);
12831 this.load(this.lastOptions);
12836 * Calls the specified function for each of the Records in the cache.
12837 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12838 * Returning <em>false</em> aborts and exits the iteration.
12839 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12841 each : function(fn, scope){
12842 this.data.each(fn, scope);
12846 * Gets all records modified since the last commit. Modified records are persisted across load operations
12847 * (e.g., during paging).
12848 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12850 getModifiedRecords : function(){
12851 return this.modified;
12855 createFilterFn : function(property, value, anyMatch){
12856 if(!value.exec){ // not a regex
12857 value = String(value);
12858 if(value.length == 0){
12861 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12863 return function(r){
12864 return value.test(r.data[property]);
12869 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12870 * @param {String} property A field on your records
12871 * @param {Number} start The record index to start at (defaults to 0)
12872 * @param {Number} end The last record index to include (defaults to length - 1)
12873 * @return {Number} The sum
12875 sum : function(property, start, end){
12876 var rs = this.data.items, v = 0;
12877 start = start || 0;
12878 end = (end || end === 0) ? end : rs.length-1;
12880 for(var i = start; i <= end; i++){
12881 v += (rs[i].data[property] || 0);
12887 * Filter the records by a specified property.
12888 * @param {String} field A field on your records
12889 * @param {String/RegExp} value Either a string that the field
12890 * should start with or a RegExp to test against the field
12891 * @param {Boolean} anyMatch True to match any part not just the beginning
12893 filter : function(property, value, anyMatch){
12894 var fn = this.createFilterFn(property, value, anyMatch);
12895 return fn ? this.filterBy(fn) : this.clearFilter();
12899 * Filter by a function. The specified function will be called with each
12900 * record in this data source. If the function returns true the record is included,
12901 * otherwise it is filtered.
12902 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12903 * @param {Object} scope (optional) The scope of the function (defaults to this)
12905 filterBy : function(fn, scope){
12906 this.snapshot = this.snapshot || this.data;
12907 this.data = this.queryBy(fn, scope||this);
12908 this.fireEvent("datachanged", this);
12912 * Query the records by a specified property.
12913 * @param {String} field A field on your records
12914 * @param {String/RegExp} value Either a string that the field
12915 * should start with or a RegExp to test against the field
12916 * @param {Boolean} anyMatch True to match any part not just the beginning
12917 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12919 query : function(property, value, anyMatch){
12920 var fn = this.createFilterFn(property, value, anyMatch);
12921 return fn ? this.queryBy(fn) : this.data.clone();
12925 * Query by a function. The specified function will be called with each
12926 * record in this data source. If the function returns true the record is included
12928 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12929 * @param {Object} scope (optional) The scope of the function (defaults to this)
12930 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12932 queryBy : function(fn, scope){
12933 var data = this.snapshot || this.data;
12934 return data.filterBy(fn, scope||this);
12938 * Collects unique values for a particular dataIndex from this store.
12939 * @param {String} dataIndex The property to collect
12940 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12941 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12942 * @return {Array} An array of the unique values
12944 collect : function(dataIndex, allowNull, bypassFilter){
12945 var d = (bypassFilter === true && this.snapshot) ?
12946 this.snapshot.items : this.data.items;
12947 var v, sv, r = [], l = {};
12948 for(var i = 0, len = d.length; i < len; i++){
12949 v = d[i].data[dataIndex];
12951 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12960 * Revert to a view of the Record cache with no filtering applied.
12961 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12963 clearFilter : function(suppressEvent){
12964 if(this.snapshot && this.snapshot != this.data){
12965 this.data = this.snapshot;
12966 delete this.snapshot;
12967 if(suppressEvent !== true){
12968 this.fireEvent("datachanged", this);
12974 afterEdit : function(record){
12975 if(this.modified.indexOf(record) == -1){
12976 this.modified.push(record);
12978 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12982 afterReject : function(record){
12983 this.modified.remove(record);
12984 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12988 afterCommit : function(record){
12989 this.modified.remove(record);
12990 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12994 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12995 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12997 commitChanges : function(){
12998 var m = this.modified.slice(0);
12999 this.modified = [];
13000 for(var i = 0, len = m.length; i < len; i++){
13006 * Cancel outstanding changes on all changed records.
13008 rejectChanges : function(){
13009 var m = this.modified.slice(0);
13010 this.modified = [];
13011 for(var i = 0, len = m.length; i < len; i++){
13016 onMetaChange : function(meta, rtype, o){
13017 this.recordType = rtype;
13018 this.fields = rtype.prototype.fields;
13019 delete this.snapshot;
13020 this.sortInfo = meta.sortInfo || this.sortInfo;
13021 this.modified = [];
13022 this.fireEvent('metachange', this, this.reader.meta);
13025 moveIndex : function(data, type)
13027 var index = this.indexOf(data);
13029 var newIndex = index + type;
13033 this.insert(newIndex, data);
13038 * Ext JS Library 1.1.1
13039 * Copyright(c) 2006-2007, Ext JS, LLC.
13041 * Originally Released Under LGPL - original licence link has changed is not relivant.
13044 * <script type="text/javascript">
13048 * @class Roo.data.SimpleStore
13049 * @extends Roo.data.Store
13050 * Small helper class to make creating Stores from Array data easier.
13051 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13052 * @cfg {Array} fields An array of field definition objects, or field name strings.
13053 * @cfg {Object} an existing reader (eg. copied from another store)
13054 * @cfg {Array} data The multi-dimensional array of data
13056 * @param {Object} config
13058 Roo.data.SimpleStore = function(config)
13060 Roo.data.SimpleStore.superclass.constructor.call(this, {
13062 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13065 Roo.data.Record.create(config.fields)
13067 proxy : new Roo.data.MemoryProxy(config.data)
13071 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13073 * Ext JS Library 1.1.1
13074 * Copyright(c) 2006-2007, Ext JS, LLC.
13076 * Originally Released Under LGPL - original licence link has changed is not relivant.
13079 * <script type="text/javascript">
13084 * @extends Roo.data.Store
13085 * @class Roo.data.JsonStore
13086 * Small helper class to make creating Stores for JSON data easier. <br/>
13088 var store = new Roo.data.JsonStore({
13089 url: 'get-images.php',
13091 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13094 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13095 * JsonReader and HttpProxy (unless inline data is provided).</b>
13096 * @cfg {Array} fields An array of field definition objects, or field name strings.
13098 * @param {Object} config
13100 Roo.data.JsonStore = function(c){
13101 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13102 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13103 reader: new Roo.data.JsonReader(c, c.fields)
13106 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13108 * Ext JS Library 1.1.1
13109 * Copyright(c) 2006-2007, Ext JS, LLC.
13111 * Originally Released Under LGPL - original licence link has changed is not relivant.
13114 * <script type="text/javascript">
13118 Roo.data.Field = function(config){
13119 if(typeof config == "string"){
13120 config = {name: config};
13122 Roo.apply(this, config);
13125 this.type = "auto";
13128 var st = Roo.data.SortTypes;
13129 // named sortTypes are supported, here we look them up
13130 if(typeof this.sortType == "string"){
13131 this.sortType = st[this.sortType];
13134 // set default sortType for strings and dates
13135 if(!this.sortType){
13138 this.sortType = st.asUCString;
13141 this.sortType = st.asDate;
13144 this.sortType = st.none;
13149 var stripRe = /[\$,%]/g;
13151 // prebuilt conversion function for this field, instead of
13152 // switching every time we're reading a value
13154 var cv, dateFormat = this.dateFormat;
13159 cv = function(v){ return v; };
13162 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13166 return v !== undefined && v !== null && v !== '' ?
13167 parseInt(String(v).replace(stripRe, ""), 10) : '';
13172 return v !== undefined && v !== null && v !== '' ?
13173 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13178 cv = function(v){ return v === true || v === "true" || v == 1; };
13185 if(v instanceof Date){
13189 if(dateFormat == "timestamp"){
13190 return new Date(v*1000);
13192 return Date.parseDate(v, dateFormat);
13194 var parsed = Date.parse(v);
13195 return parsed ? new Date(parsed) : null;
13204 Roo.data.Field.prototype = {
13212 * Ext JS Library 1.1.1
13213 * Copyright(c) 2006-2007, Ext JS, LLC.
13215 * Originally Released Under LGPL - original licence link has changed is not relivant.
13218 * <script type="text/javascript">
13221 // Base class for reading structured data from a data source. This class is intended to be
13222 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13225 * @class Roo.data.DataReader
13226 * Base class for reading structured data from a data source. This class is intended to be
13227 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13230 Roo.data.DataReader = function(meta, recordType){
13234 this.recordType = recordType instanceof Array ?
13235 Roo.data.Record.create(recordType) : recordType;
13238 Roo.data.DataReader.prototype = {
13241 readerType : 'Data',
13243 * Create an empty record
13244 * @param {Object} data (optional) - overlay some values
13245 * @return {Roo.data.Record} record created.
13247 newRow : function(d) {
13249 this.recordType.prototype.fields.each(function(c) {
13251 case 'int' : da[c.name] = 0; break;
13252 case 'date' : da[c.name] = new Date(); break;
13253 case 'float' : da[c.name] = 0.0; break;
13254 case 'boolean' : da[c.name] = false; break;
13255 default : da[c.name] = ""; break;
13259 return new this.recordType(Roo.apply(da, d));
13265 * Ext JS Library 1.1.1
13266 * Copyright(c) 2006-2007, Ext JS, LLC.
13268 * Originally Released Under LGPL - original licence link has changed is not relivant.
13271 * <script type="text/javascript">
13275 * @class Roo.data.DataProxy
13276 * @extends Roo.data.Observable
13277 * This class is an abstract base class for implementations which provide retrieval of
13278 * unformatted data objects.<br>
13280 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13281 * (of the appropriate type which knows how to parse the data object) to provide a block of
13282 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13284 * Custom implementations must implement the load method as described in
13285 * {@link Roo.data.HttpProxy#load}.
13287 Roo.data.DataProxy = function(){
13290 * @event beforeload
13291 * Fires before a network request is made to retrieve a data object.
13292 * @param {Object} This DataProxy object.
13293 * @param {Object} params The params parameter to the load function.
13298 * Fires before the load method's callback is called.
13299 * @param {Object} This DataProxy object.
13300 * @param {Object} o The data object.
13301 * @param {Object} arg The callback argument object passed to the load function.
13305 * @event loadexception
13306 * Fires if an Exception occurs during data retrieval.
13307 * @param {Object} This DataProxy object.
13308 * @param {Object} o The data object.
13309 * @param {Object} arg The callback argument object passed to the load function.
13310 * @param {Object} e The Exception.
13312 loadexception : true
13314 Roo.data.DataProxy.superclass.constructor.call(this);
13317 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13320 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13324 * Ext JS Library 1.1.1
13325 * Copyright(c) 2006-2007, Ext JS, LLC.
13327 * Originally Released Under LGPL - original licence link has changed is not relivant.
13330 * <script type="text/javascript">
13333 * @class Roo.data.MemoryProxy
13334 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13335 * to the Reader when its load method is called.
13337 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13339 Roo.data.MemoryProxy = function(data){
13343 Roo.data.MemoryProxy.superclass.constructor.call(this);
13347 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13350 * Load data from the requested source (in this case an in-memory
13351 * data object passed to the constructor), read the data object into
13352 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13353 * process that block using the passed callback.
13354 * @param {Object} params This parameter is not used by the MemoryProxy class.
13355 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13356 * object into a block of Roo.data.Records.
13357 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13358 * The function must be passed <ul>
13359 * <li>The Record block object</li>
13360 * <li>The "arg" argument from the load function</li>
13361 * <li>A boolean success indicator</li>
13363 * @param {Object} scope The scope in which to call the callback
13364 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13366 load : function(params, reader, callback, scope, arg){
13367 params = params || {};
13370 result = reader.readRecords(params.data ? params.data :this.data);
13372 this.fireEvent("loadexception", this, arg, null, e);
13373 callback.call(scope, null, arg, false);
13376 callback.call(scope, result, arg, true);
13380 update : function(params, records){
13385 * Ext JS Library 1.1.1
13386 * Copyright(c) 2006-2007, Ext JS, LLC.
13388 * Originally Released Under LGPL - original licence link has changed is not relivant.
13391 * <script type="text/javascript">
13394 * @class Roo.data.HttpProxy
13395 * @extends Roo.data.DataProxy
13396 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13397 * configured to reference a certain URL.<br><br>
13399 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13400 * from which the running page was served.<br><br>
13402 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13404 * Be aware that to enable the browser to parse an XML document, the server must set
13405 * the Content-Type header in the HTTP response to "text/xml".
13407 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13408 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
13409 * will be used to make the request.
13411 Roo.data.HttpProxy = function(conn){
13412 Roo.data.HttpProxy.superclass.constructor.call(this);
13413 // is conn a conn config or a real conn?
13415 this.useAjax = !conn || !conn.events;
13419 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13420 // thse are take from connection...
13423 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13426 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13427 * extra parameters to each request made by this object. (defaults to undefined)
13430 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13431 * to each request made by this object. (defaults to undefined)
13434 * @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)
13437 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13440 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13446 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13450 * Return the {@link Roo.data.Connection} object being used by this Proxy.
13451 * @return {Connection} The Connection object. This object may be used to subscribe to events on
13452 * a finer-grained basis than the DataProxy events.
13454 getConnection : function(){
13455 return this.useAjax ? Roo.Ajax : this.conn;
13459 * Load data from the configured {@link Roo.data.Connection}, read the data object into
13460 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13461 * process that block using the passed callback.
13462 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13463 * for the request to the remote server.
13464 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13465 * object into a block of Roo.data.Records.
13466 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13467 * The function must be passed <ul>
13468 * <li>The Record block object</li>
13469 * <li>The "arg" argument from the load function</li>
13470 * <li>A boolean success indicator</li>
13472 * @param {Object} scope The scope in which to call the callback
13473 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13475 load : function(params, reader, callback, scope, arg){
13476 if(this.fireEvent("beforeload", this, params) !== false){
13478 params : params || {},
13480 callback : callback,
13485 callback : this.loadResponse,
13489 Roo.applyIf(o, this.conn);
13490 if(this.activeRequest){
13491 Roo.Ajax.abort(this.activeRequest);
13493 this.activeRequest = Roo.Ajax.request(o);
13495 this.conn.request(o);
13498 callback.call(scope||this, null, arg, false);
13503 loadResponse : function(o, success, response){
13504 delete this.activeRequest;
13506 this.fireEvent("loadexception", this, o, response);
13507 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13512 result = o.reader.read(response);
13514 this.fireEvent("loadexception", this, o, response, e);
13515 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13519 this.fireEvent("load", this, o, o.request.arg);
13520 o.request.callback.call(o.request.scope, result, o.request.arg, true);
13524 update : function(dataSet){
13529 updateResponse : function(dataSet){
13534 * Ext JS Library 1.1.1
13535 * Copyright(c) 2006-2007, Ext JS, LLC.
13537 * Originally Released Under LGPL - original licence link has changed is not relivant.
13540 * <script type="text/javascript">
13544 * @class Roo.data.ScriptTagProxy
13545 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13546 * other than the originating domain of the running page.<br><br>
13548 * <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
13549 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13551 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13552 * source code that is used as the source inside a <script> tag.<br><br>
13554 * In order for the browser to process the returned data, the server must wrap the data object
13555 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13556 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13557 * depending on whether the callback name was passed:
13560 boolean scriptTag = false;
13561 String cb = request.getParameter("callback");
13564 response.setContentType("text/javascript");
13566 response.setContentType("application/x-json");
13568 Writer out = response.getWriter();
13570 out.write(cb + "(");
13572 out.print(dataBlock.toJsonString());
13579 * @param {Object} config A configuration object.
13581 Roo.data.ScriptTagProxy = function(config){
13582 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13583 Roo.apply(this, config);
13584 this.head = document.getElementsByTagName("head")[0];
13587 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13589 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13591 * @cfg {String} url The URL from which to request the data object.
13594 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13598 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13599 * the server the name of the callback function set up by the load call to process the returned data object.
13600 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13601 * javascript output which calls this named function passing the data object as its only parameter.
13603 callbackParam : "callback",
13605 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13606 * name to the request.
13611 * Load data from the configured URL, read the data object into
13612 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13613 * process that block using the passed callback.
13614 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13615 * for the request to the remote server.
13616 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13617 * object into a block of Roo.data.Records.
13618 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13619 * The function must be passed <ul>
13620 * <li>The Record block object</li>
13621 * <li>The "arg" argument from the load function</li>
13622 * <li>A boolean success indicator</li>
13624 * @param {Object} scope The scope in which to call the callback
13625 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13627 load : function(params, reader, callback, scope, arg){
13628 if(this.fireEvent("beforeload", this, params) !== false){
13630 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13632 var url = this.url;
13633 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13635 url += "&_dc=" + (new Date().getTime());
13637 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13640 cb : "stcCallback"+transId,
13641 scriptId : "stcScript"+transId,
13645 callback : callback,
13651 window[trans.cb] = function(o){
13652 conn.handleResponse(o, trans);
13655 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13657 if(this.autoAbort !== false){
13661 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13663 var script = document.createElement("script");
13664 script.setAttribute("src", url);
13665 script.setAttribute("type", "text/javascript");
13666 script.setAttribute("id", trans.scriptId);
13667 this.head.appendChild(script);
13669 this.trans = trans;
13671 callback.call(scope||this, null, arg, false);
13676 isLoading : function(){
13677 return this.trans ? true : false;
13681 * Abort the current server request.
13683 abort : function(){
13684 if(this.isLoading()){
13685 this.destroyTrans(this.trans);
13690 destroyTrans : function(trans, isLoaded){
13691 this.head.removeChild(document.getElementById(trans.scriptId));
13692 clearTimeout(trans.timeoutId);
13694 window[trans.cb] = undefined;
13696 delete window[trans.cb];
13699 // if hasn't been loaded, wait for load to remove it to prevent script error
13700 window[trans.cb] = function(){
13701 window[trans.cb] = undefined;
13703 delete window[trans.cb];
13710 handleResponse : function(o, trans){
13711 this.trans = false;
13712 this.destroyTrans(trans, true);
13715 result = trans.reader.readRecords(o);
13717 this.fireEvent("loadexception", this, o, trans.arg, e);
13718 trans.callback.call(trans.scope||window, null, trans.arg, false);
13721 this.fireEvent("load", this, o, trans.arg);
13722 trans.callback.call(trans.scope||window, result, trans.arg, true);
13726 handleFailure : function(trans){
13727 this.trans = false;
13728 this.destroyTrans(trans, false);
13729 this.fireEvent("loadexception", this, null, trans.arg);
13730 trans.callback.call(trans.scope||window, null, trans.arg, false);
13734 * Ext JS Library 1.1.1
13735 * Copyright(c) 2006-2007, Ext JS, LLC.
13737 * Originally Released Under LGPL - original licence link has changed is not relivant.
13740 * <script type="text/javascript">
13744 * @class Roo.data.JsonReader
13745 * @extends Roo.data.DataReader
13746 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13747 * based on mappings in a provided Roo.data.Record constructor.
13749 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13750 * in the reply previously.
13755 var RecordDef = Roo.data.Record.create([
13756 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13757 {name: 'occupation'} // This field will use "occupation" as the mapping.
13759 var myReader = new Roo.data.JsonReader({
13760 totalProperty: "results", // The property which contains the total dataset size (optional)
13761 root: "rows", // The property which contains an Array of row objects
13762 id: "id" // The property within each row object that provides an ID for the record (optional)
13766 * This would consume a JSON file like this:
13768 { 'results': 2, 'rows': [
13769 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13770 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13773 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13774 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13775 * paged from the remote server.
13776 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13777 * @cfg {String} root name of the property which contains the Array of row objects.
13778 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13779 * @cfg {Array} fields Array of field definition objects
13781 * Create a new JsonReader
13782 * @param {Object} meta Metadata configuration options
13783 * @param {Object} recordType Either an Array of field definition objects,
13784 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13786 Roo.data.JsonReader = function(meta, recordType){
13789 // set some defaults:
13790 Roo.applyIf(meta, {
13791 totalProperty: 'total',
13792 successProperty : 'success',
13797 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13799 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13801 readerType : 'Json',
13804 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13805 * Used by Store query builder to append _requestMeta to params.
13808 metaFromRemote : false,
13810 * This method is only used by a DataProxy which has retrieved data from a remote server.
13811 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13812 * @return {Object} data A data block which is used by an Roo.data.Store object as
13813 * a cache of Roo.data.Records.
13815 read : function(response){
13816 var json = response.responseText;
13818 var o = /* eval:var:o */ eval("("+json+")");
13820 throw {message: "JsonReader.read: Json object not found"};
13826 this.metaFromRemote = true;
13827 this.meta = o.metaData;
13828 this.recordType = Roo.data.Record.create(o.metaData.fields);
13829 this.onMetaChange(this.meta, this.recordType, o);
13831 return this.readRecords(o);
13834 // private function a store will implement
13835 onMetaChange : function(meta, recordType, o){
13842 simpleAccess: function(obj, subsc) {
13849 getJsonAccessor: function(){
13851 return function(expr) {
13853 return(re.test(expr))
13854 ? new Function("obj", "return obj." + expr)
13859 return Roo.emptyFn;
13864 * Create a data block containing Roo.data.Records from an XML document.
13865 * @param {Object} o An object which contains an Array of row objects in the property specified
13866 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13867 * which contains the total size of the dataset.
13868 * @return {Object} data A data block which is used by an Roo.data.Store object as
13869 * a cache of Roo.data.Records.
13871 readRecords : function(o){
13873 * After any data loads, the raw JSON data is available for further custom processing.
13877 var s = this.meta, Record = this.recordType,
13878 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13880 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13882 if(s.totalProperty) {
13883 this.getTotal = this.getJsonAccessor(s.totalProperty);
13885 if(s.successProperty) {
13886 this.getSuccess = this.getJsonAccessor(s.successProperty);
13888 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13890 var g = this.getJsonAccessor(s.id);
13891 this.getId = function(rec) {
13893 return (r === undefined || r === "") ? null : r;
13896 this.getId = function(){return null;};
13899 for(var jj = 0; jj < fl; jj++){
13901 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13902 this.ef[jj] = this.getJsonAccessor(map);
13906 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13907 if(s.totalProperty){
13908 var vt = parseInt(this.getTotal(o), 10);
13913 if(s.successProperty){
13914 var vs = this.getSuccess(o);
13915 if(vs === false || vs === 'false'){
13920 for(var i = 0; i < c; i++){
13923 var id = this.getId(n);
13924 for(var j = 0; j < fl; j++){
13926 var v = this.ef[j](n);
13928 Roo.log('missing convert for ' + f.name);
13932 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13934 var record = new Record(values, id);
13936 records[i] = record;
13942 totalRecords : totalRecords
13945 // used when loading children.. @see loadDataFromChildren
13946 toLoadData: function(rec)
13948 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13949 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13950 return { data : data, total : data.length };
13955 * Ext JS Library 1.1.1
13956 * Copyright(c) 2006-2007, Ext JS, LLC.
13958 * Originally Released Under LGPL - original licence link has changed is not relivant.
13961 * <script type="text/javascript">
13965 * @class Roo.data.ArrayReader
13966 * @extends Roo.data.DataReader
13967 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13968 * Each element of that Array represents a row of data fields. The
13969 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13970 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13974 var RecordDef = Roo.data.Record.create([
13975 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13976 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13978 var myReader = new Roo.data.ArrayReader({
13979 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13983 * This would consume an Array like this:
13985 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13989 * Create a new JsonReader
13990 * @param {Object} meta Metadata configuration options.
13991 * @param {Object|Array} recordType Either an Array of field definition objects
13993 * @cfg {Array} fields Array of field definition objects
13994 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13995 * as specified to {@link Roo.data.Record#create},
13996 * or an {@link Roo.data.Record} object
13999 * created using {@link Roo.data.Record#create}.
14001 Roo.data.ArrayReader = function(meta, recordType)
14003 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14006 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14009 * Create a data block containing Roo.data.Records from an XML document.
14010 * @param {Object} o An Array of row objects which represents the dataset.
14011 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14012 * a cache of Roo.data.Records.
14014 readRecords : function(o)
14016 var sid = this.meta ? this.meta.id : null;
14017 var recordType = this.recordType, fields = recordType.prototype.fields;
14020 for(var i = 0; i < root.length; i++){
14023 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14024 for(var j = 0, jlen = fields.length; j < jlen; j++){
14025 var f = fields.items[j];
14026 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14027 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14029 values[f.name] = v;
14031 var record = new recordType(values, id);
14033 records[records.length] = record;
14037 totalRecords : records.length
14040 // used when loading children.. @see loadDataFromChildren
14041 toLoadData: function(rec)
14043 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14044 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14055 * @class Roo.bootstrap.ComboBox
14056 * @extends Roo.bootstrap.TriggerField
14057 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14058 * @cfg {Boolean} append (true|false) default false
14059 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14060 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14061 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14062 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14063 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14064 * @cfg {Boolean} animate default true
14065 * @cfg {Boolean} emptyResultText only for touch device
14066 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14067 * @cfg {String} emptyTitle default ''
14069 * Create a new ComboBox.
14070 * @param {Object} config Configuration options
14072 Roo.bootstrap.ComboBox = function(config){
14073 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14077 * Fires when the dropdown list is expanded
14078 * @param {Roo.bootstrap.ComboBox} combo This combo box
14083 * Fires when the dropdown list is collapsed
14084 * @param {Roo.bootstrap.ComboBox} combo This combo box
14088 * @event beforeselect
14089 * Fires before a list item is selected. Return false to cancel the selection.
14090 * @param {Roo.bootstrap.ComboBox} combo This combo box
14091 * @param {Roo.data.Record} record The data record returned from the underlying store
14092 * @param {Number} index The index of the selected item in the dropdown list
14094 'beforeselect' : true,
14097 * Fires when a list item is selected
14098 * @param {Roo.bootstrap.ComboBox} combo This combo box
14099 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14100 * @param {Number} index The index of the selected item in the dropdown list
14104 * @event beforequery
14105 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14106 * The event object passed has these properties:
14107 * @param {Roo.bootstrap.ComboBox} combo This combo box
14108 * @param {String} query The query
14109 * @param {Boolean} forceAll true to force "all" query
14110 * @param {Boolean} cancel true to cancel the query
14111 * @param {Object} e The query event object
14113 'beforequery': true,
14116 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14117 * @param {Roo.bootstrap.ComboBox} combo This combo box
14122 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14123 * @param {Roo.bootstrap.ComboBox} combo This combo box
14124 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14129 * Fires when the remove value from the combobox array
14130 * @param {Roo.bootstrap.ComboBox} combo This combo box
14134 * @event afterremove
14135 * Fires when the remove value from the combobox array
14136 * @param {Roo.bootstrap.ComboBox} combo This combo box
14138 'afterremove' : true,
14140 * @event specialfilter
14141 * Fires when specialfilter
14142 * @param {Roo.bootstrap.ComboBox} combo This combo box
14144 'specialfilter' : true,
14147 * Fires when tick the element
14148 * @param {Roo.bootstrap.ComboBox} combo This combo box
14152 * @event touchviewdisplay
14153 * Fires when touch view require special display (default is using displayField)
14154 * @param {Roo.bootstrap.ComboBox} combo This combo box
14155 * @param {Object} cfg set html .
14157 'touchviewdisplay' : true
14162 this.tickItems = [];
14164 this.selectedIndex = -1;
14165 if(this.mode == 'local'){
14166 if(config.queryDelay === undefined){
14167 this.queryDelay = 10;
14169 if(config.minChars === undefined){
14175 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14178 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14179 * rendering into an Roo.Editor, defaults to false)
14182 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14183 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14186 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14189 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14190 * the dropdown list (defaults to undefined, with no header element)
14194 * @cfg {String/Roo.Template} tpl The template to use to render the output
14198 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14200 listWidth: undefined,
14202 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14203 * mode = 'remote' or 'text' if mode = 'local')
14205 displayField: undefined,
14208 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14209 * mode = 'remote' or 'value' if mode = 'local').
14210 * Note: use of a valueField requires the user make a selection
14211 * in order for a value to be mapped.
14213 valueField: undefined,
14215 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14220 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14221 * field's data value (defaults to the underlying DOM element's name)
14223 hiddenName: undefined,
14225 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14229 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14231 selectedClass: 'active',
14234 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14238 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14239 * anchor positions (defaults to 'tl-bl')
14241 listAlign: 'tl-bl?',
14243 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14247 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14248 * query specified by the allQuery config option (defaults to 'query')
14250 triggerAction: 'query',
14252 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14253 * (defaults to 4, does not apply if editable = false)
14257 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14258 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14262 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14263 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14267 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14268 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14272 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14273 * when editable = true (defaults to false)
14275 selectOnFocus:false,
14277 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14279 queryParam: 'query',
14281 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14282 * when mode = 'remote' (defaults to 'Loading...')
14284 loadingText: 'Loading...',
14286 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14290 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14294 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14295 * traditional select (defaults to true)
14299 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14303 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14307 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14308 * listWidth has a higher value)
14312 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14313 * allow the user to set arbitrary text into the field (defaults to false)
14315 forceSelection:false,
14317 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14318 * if typeAhead = true (defaults to 250)
14320 typeAheadDelay : 250,
14322 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14323 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14325 valueNotFoundText : undefined,
14327 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14329 blockFocus : false,
14332 * @cfg {Boolean} disableClear Disable showing of clear button.
14334 disableClear : false,
14336 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14338 alwaysQuery : false,
14341 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14346 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14348 invalidClass : "has-warning",
14351 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14353 validClass : "has-success",
14356 * @cfg {Boolean} specialFilter (true|false) special filter default false
14358 specialFilter : false,
14361 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14363 mobileTouchView : true,
14366 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14368 useNativeIOS : false,
14371 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14373 mobile_restrict_height : false,
14375 ios_options : false,
14387 btnPosition : 'right',
14388 triggerList : true,
14389 showToggleBtn : true,
14391 emptyResultText: 'Empty',
14392 triggerText : 'Select',
14395 // element that contains real text value.. (when hidden is used..)
14397 getAutoCreate : function()
14402 * Render classic select for iso
14405 if(Roo.isIOS && this.useNativeIOS){
14406 cfg = this.getAutoCreateNativeIOS();
14414 if(Roo.isTouch && this.mobileTouchView){
14415 cfg = this.getAutoCreateTouchView();
14422 if(!this.tickable){
14423 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14428 * ComboBox with tickable selections
14431 var align = this.labelAlign || this.parentLabelAlign();
14434 cls : 'form-group roo-combobox-tickable' //input-group
14437 var btn_text_select = '';
14438 var btn_text_done = '';
14439 var btn_text_cancel = '';
14441 if (this.btn_text_show) {
14442 btn_text_select = 'Select';
14443 btn_text_done = 'Done';
14444 btn_text_cancel = 'Cancel';
14449 cls : 'tickable-buttons',
14454 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14455 //html : this.triggerText
14456 html: btn_text_select
14462 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14464 html: btn_text_done
14470 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14472 html: btn_text_cancel
14478 buttons.cn.unshift({
14480 cls: 'roo-select2-search-field-input'
14486 Roo.each(buttons.cn, function(c){
14488 c.cls += ' btn-' + _this.size;
14491 if (_this.disabled) {
14498 style : 'display: contents',
14503 cls: 'form-hidden-field'
14507 cls: 'roo-select2-choices',
14511 cls: 'roo-select2-search-field',
14522 cls: 'roo-select2-container input-group roo-select2-container-multi',
14528 // cls: 'typeahead typeahead-long dropdown-menu',
14529 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
14534 if(this.hasFeedback && !this.allowBlank){
14538 cls: 'glyphicon form-control-feedback'
14541 combobox.cn.push(feedback);
14546 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14547 tooltip : 'This field is required'
14549 if (Roo.bootstrap.version == 4) {
14552 style : 'display:none'
14555 if (align ==='left' && this.fieldLabel.length) {
14557 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14564 cls : 'control-label col-form-label',
14565 html : this.fieldLabel
14577 var labelCfg = cfg.cn[1];
14578 var contentCfg = cfg.cn[2];
14581 if(this.indicatorpos == 'right'){
14587 cls : 'control-label col-form-label',
14591 html : this.fieldLabel
14607 labelCfg = cfg.cn[0];
14608 contentCfg = cfg.cn[1];
14612 if(this.labelWidth > 12){
14613 labelCfg.style = "width: " + this.labelWidth + 'px';
14616 if(this.labelWidth < 13 && this.labelmd == 0){
14617 this.labelmd = this.labelWidth;
14620 if(this.labellg > 0){
14621 labelCfg.cls += ' col-lg-' + this.labellg;
14622 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14625 if(this.labelmd > 0){
14626 labelCfg.cls += ' col-md-' + this.labelmd;
14627 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14630 if(this.labelsm > 0){
14631 labelCfg.cls += ' col-sm-' + this.labelsm;
14632 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14635 if(this.labelxs > 0){
14636 labelCfg.cls += ' col-xs-' + this.labelxs;
14637 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14641 } else if ( this.fieldLabel.length) {
14642 // Roo.log(" label");
14647 //cls : 'input-group-addon',
14648 html : this.fieldLabel
14653 if(this.indicatorpos == 'right'){
14657 //cls : 'input-group-addon',
14658 html : this.fieldLabel
14668 // Roo.log(" no label && no align");
14675 ['xs','sm','md','lg'].map(function(size){
14676 if (settings[size]) {
14677 cfg.cls += ' col-' + size + '-' + settings[size];
14685 _initEventsCalled : false,
14688 initEvents: function()
14690 if (this._initEventsCalled) { // as we call render... prevent looping...
14693 this._initEventsCalled = true;
14696 throw "can not find store for combo";
14699 this.indicator = this.indicatorEl();
14701 this.store = Roo.factory(this.store, Roo.data);
14702 this.store.parent = this;
14704 // if we are building from html. then this element is so complex, that we can not really
14705 // use the rendered HTML.
14706 // so we have to trash and replace the previous code.
14707 if (Roo.XComponent.build_from_html) {
14708 // remove this element....
14709 var e = this.el.dom, k=0;
14710 while (e ) { e = e.previousSibling; ++k;}
14715 this.rendered = false;
14717 this.render(this.parent().getChildContainer(true), k);
14720 if(Roo.isIOS && this.useNativeIOS){
14721 this.initIOSView();
14729 if(Roo.isTouch && this.mobileTouchView){
14730 this.initTouchView();
14735 this.initTickableEvents();
14739 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14741 if(this.hiddenName){
14743 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14745 this.hiddenField.dom.value =
14746 this.hiddenValue !== undefined ? this.hiddenValue :
14747 this.value !== undefined ? this.value : '';
14749 // prevent input submission
14750 this.el.dom.removeAttribute('name');
14751 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14756 // this.el.dom.setAttribute('autocomplete', 'off');
14759 var cls = 'x-combo-list';
14761 //this.list = new Roo.Layer({
14762 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14768 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14769 _this.list.setWidth(lw);
14772 this.list.on('mouseover', this.onViewOver, this);
14773 this.list.on('mousemove', this.onViewMove, this);
14774 this.list.on('scroll', this.onViewScroll, this);
14777 this.list.swallowEvent('mousewheel');
14778 this.assetHeight = 0;
14781 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14782 this.assetHeight += this.header.getHeight();
14785 this.innerList = this.list.createChild({cls:cls+'-inner'});
14786 this.innerList.on('mouseover', this.onViewOver, this);
14787 this.innerList.on('mousemove', this.onViewMove, this);
14788 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14790 if(this.allowBlank && !this.pageSize && !this.disableClear){
14791 this.footer = this.list.createChild({cls:cls+'-ft'});
14792 this.pageTb = new Roo.Toolbar(this.footer);
14796 this.footer = this.list.createChild({cls:cls+'-ft'});
14797 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14798 {pageSize: this.pageSize});
14802 if (this.pageTb && this.allowBlank && !this.disableClear) {
14804 this.pageTb.add(new Roo.Toolbar.Fill(), {
14805 cls: 'x-btn-icon x-btn-clear',
14807 handler: function()
14810 _this.clearValue();
14811 _this.onSelect(false, -1);
14816 this.assetHeight += this.footer.getHeight();
14821 this.tpl = Roo.bootstrap.version == 4 ?
14822 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14823 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14826 this.view = new Roo.View(this.list, this.tpl, {
14827 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14829 //this.view.wrapEl.setDisplayed(false);
14830 this.view.on('click', this.onViewClick, this);
14833 this.store.on('beforeload', this.onBeforeLoad, this);
14834 this.store.on('load', this.onLoad, this);
14835 this.store.on('loadexception', this.onLoadException, this);
14837 if(this.resizable){
14838 this.resizer = new Roo.Resizable(this.list, {
14839 pinned:true, handles:'se'
14841 this.resizer.on('resize', function(r, w, h){
14842 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14843 this.listWidth = w;
14844 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14845 this.restrictHeight();
14847 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14850 if(!this.editable){
14851 this.editable = true;
14852 this.setEditable(false);
14857 if (typeof(this.events.add.listeners) != 'undefined') {
14859 this.addicon = this.wrap.createChild(
14860 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14862 this.addicon.on('click', function(e) {
14863 this.fireEvent('add', this);
14866 if (typeof(this.events.edit.listeners) != 'undefined') {
14868 this.editicon = this.wrap.createChild(
14869 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14870 if (this.addicon) {
14871 this.editicon.setStyle('margin-left', '40px');
14873 this.editicon.on('click', function(e) {
14875 // we fire even if inothing is selected..
14876 this.fireEvent('edit', this, this.lastData );
14882 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14883 "up" : function(e){
14884 this.inKeyMode = true;
14888 "down" : function(e){
14889 if(!this.isExpanded()){
14890 this.onTriggerClick();
14892 this.inKeyMode = true;
14897 "enter" : function(e){
14898 // this.onViewClick();
14902 if(this.fireEvent("specialkey", this, e)){
14903 this.onViewClick(false);
14909 "esc" : function(e){
14913 "tab" : function(e){
14916 if(this.fireEvent("specialkey", this, e)){
14917 this.onViewClick(false);
14925 doRelay : function(foo, bar, hname){
14926 if(hname == 'down' || this.scope.isExpanded()){
14927 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14936 this.queryDelay = Math.max(this.queryDelay || 10,
14937 this.mode == 'local' ? 10 : 250);
14940 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14942 if(this.typeAhead){
14943 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14945 if(this.editable !== false){
14946 this.inputEl().on("keyup", this.onKeyUp, this);
14948 if(this.forceSelection){
14949 this.inputEl().on('blur', this.doForce, this);
14953 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14954 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14958 initTickableEvents: function()
14962 if(this.hiddenName){
14964 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14966 this.hiddenField.dom.value =
14967 this.hiddenValue !== undefined ? this.hiddenValue :
14968 this.value !== undefined ? this.value : '';
14970 // prevent input submission
14971 this.el.dom.removeAttribute('name');
14972 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14977 // this.list = this.el.select('ul.dropdown-menu',true).first();
14979 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14980 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14981 if(this.triggerList){
14982 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14985 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14986 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14988 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14989 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14991 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14992 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14994 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14995 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14996 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14999 this.cancelBtn.hide();
15004 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15005 _this.list.setWidth(lw);
15008 this.list.on('mouseover', this.onViewOver, this);
15009 this.list.on('mousemove', this.onViewMove, this);
15011 this.list.on('scroll', this.onViewScroll, this);
15014 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15015 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15018 this.view = new Roo.View(this.list, this.tpl, {
15023 selectedClass: this.selectedClass
15026 //this.view.wrapEl.setDisplayed(false);
15027 this.view.on('click', this.onViewClick, this);
15031 this.store.on('beforeload', this.onBeforeLoad, this);
15032 this.store.on('load', this.onLoad, this);
15033 this.store.on('loadexception', this.onLoadException, this);
15036 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15037 "up" : function(e){
15038 this.inKeyMode = true;
15042 "down" : function(e){
15043 this.inKeyMode = true;
15047 "enter" : function(e){
15048 if(this.fireEvent("specialkey", this, e)){
15049 this.onViewClick(false);
15055 "esc" : function(e){
15056 this.onTickableFooterButtonClick(e, false, false);
15059 "tab" : function(e){
15060 this.fireEvent("specialkey", this, e);
15062 this.onTickableFooterButtonClick(e, false, false);
15069 doRelay : function(e, fn, key){
15070 if(this.scope.isExpanded()){
15071 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15080 this.queryDelay = Math.max(this.queryDelay || 10,
15081 this.mode == 'local' ? 10 : 250);
15084 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15086 if(this.typeAhead){
15087 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15090 if(this.editable !== false){
15091 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15094 this.indicator = this.indicatorEl();
15096 if(this.indicator){
15097 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15098 this.indicator.hide();
15103 onDestroy : function(){
15105 this.view.setStore(null);
15106 this.view.el.removeAllListeners();
15107 this.view.el.remove();
15108 this.view.purgeListeners();
15111 this.list.dom.innerHTML = '';
15115 this.store.un('beforeload', this.onBeforeLoad, this);
15116 this.store.un('load', this.onLoad, this);
15117 this.store.un('loadexception', this.onLoadException, this);
15119 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15123 fireKey : function(e){
15124 if(e.isNavKeyPress() && !this.list.isVisible()){
15125 this.fireEvent("specialkey", this, e);
15130 onResize: function(w, h){
15131 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15133 // if(typeof w != 'number'){
15134 // // we do not handle it!?!?
15137 // var tw = this.trigger.getWidth();
15138 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15139 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15141 // this.inputEl().setWidth( this.adjustWidth('input', x));
15143 // //this.trigger.setStyle('left', x+'px');
15145 // if(this.list && this.listWidth === undefined){
15146 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15147 // this.list.setWidth(lw);
15148 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15156 * Allow or prevent the user from directly editing the field text. If false is passed,
15157 * the user will only be able to select from the items defined in the dropdown list. This method
15158 * is the runtime equivalent of setting the 'editable' config option at config time.
15159 * @param {Boolean} value True to allow the user to directly edit the field text
15161 setEditable : function(value){
15162 if(value == this.editable){
15165 this.editable = value;
15167 this.inputEl().dom.setAttribute('readOnly', true);
15168 this.inputEl().on('mousedown', this.onTriggerClick, this);
15169 this.inputEl().addClass('x-combo-noedit');
15171 this.inputEl().dom.setAttribute('readOnly', false);
15172 this.inputEl().un('mousedown', this.onTriggerClick, this);
15173 this.inputEl().removeClass('x-combo-noedit');
15179 onBeforeLoad : function(combo,opts){
15180 if(!this.hasFocus){
15184 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15186 this.restrictHeight();
15187 this.selectedIndex = -1;
15191 onLoad : function(){
15193 this.hasQuery = false;
15195 if(!this.hasFocus){
15199 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15200 this.loading.hide();
15203 if(this.store.getCount() > 0){
15206 this.restrictHeight();
15207 if(this.lastQuery == this.allQuery){
15208 if(this.editable && !this.tickable){
15209 this.inputEl().dom.select();
15213 !this.selectByValue(this.value, true) &&
15216 !this.store.lastOptions ||
15217 typeof(this.store.lastOptions.add) == 'undefined' ||
15218 this.store.lastOptions.add != true
15221 this.select(0, true);
15224 if(this.autoFocus){
15227 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15228 this.taTask.delay(this.typeAheadDelay);
15232 this.onEmptyResults();
15238 onLoadException : function()
15240 this.hasQuery = false;
15242 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15243 this.loading.hide();
15246 if(this.tickable && this.editable){
15251 // only causes errors at present
15252 //Roo.log(this.store.reader.jsonData);
15253 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15255 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15261 onTypeAhead : function(){
15262 if(this.store.getCount() > 0){
15263 var r = this.store.getAt(0);
15264 var newValue = r.data[this.displayField];
15265 var len = newValue.length;
15266 var selStart = this.getRawValue().length;
15268 if(selStart != len){
15269 this.setRawValue(newValue);
15270 this.selectText(selStart, newValue.length);
15276 onSelect : function(record, index){
15278 if(this.fireEvent('beforeselect', this, record, index) !== false){
15280 this.setFromData(index > -1 ? record.data : false);
15283 this.fireEvent('select', this, record, index);
15288 * Returns the currently selected field value or empty string if no value is set.
15289 * @return {String} value The selected value
15291 getValue : function()
15293 if(Roo.isIOS && this.useNativeIOS){
15294 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15298 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15301 if(this.valueField){
15302 return typeof this.value != 'undefined' ? this.value : '';
15304 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15308 getRawValue : function()
15310 if(Roo.isIOS && this.useNativeIOS){
15311 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15314 var v = this.inputEl().getValue();
15320 * Clears any text/value currently set in the field
15322 clearValue : function(){
15324 if(this.hiddenField){
15325 this.hiddenField.dom.value = '';
15328 this.setRawValue('');
15329 this.lastSelectionText = '';
15330 this.lastData = false;
15332 var close = this.closeTriggerEl();
15343 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15344 * will be displayed in the field. If the value does not match the data value of an existing item,
15345 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15346 * Otherwise the field will be blank (although the value will still be set).
15347 * @param {String} value The value to match
15349 setValue : function(v)
15351 if(Roo.isIOS && this.useNativeIOS){
15352 this.setIOSValue(v);
15362 if(this.valueField){
15363 var r = this.findRecord(this.valueField, v);
15365 text = r.data[this.displayField];
15366 }else if(this.valueNotFoundText !== undefined){
15367 text = this.valueNotFoundText;
15370 this.lastSelectionText = text;
15371 if(this.hiddenField){
15372 this.hiddenField.dom.value = v;
15374 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15377 var close = this.closeTriggerEl();
15380 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15386 * @property {Object} the last set data for the element
15391 * Sets the value of the field based on a object which is related to the record format for the store.
15392 * @param {Object} value the value to set as. or false on reset?
15394 setFromData : function(o){
15401 var dv = ''; // display value
15402 var vv = ''; // value value..
15404 if (this.displayField) {
15405 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15407 // this is an error condition!!!
15408 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15411 if(this.valueField){
15412 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15415 var close = this.closeTriggerEl();
15418 if(dv.length || vv * 1 > 0){
15420 this.blockFocus=true;
15426 if(this.hiddenField){
15427 this.hiddenField.dom.value = vv;
15429 this.lastSelectionText = dv;
15430 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15434 // no hidden field.. - we store the value in 'value', but still display
15435 // display field!!!!
15436 this.lastSelectionText = dv;
15437 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15444 reset : function(){
15445 // overridden so that last data is reset..
15452 this.setValue(this.originalValue);
15453 //this.clearInvalid();
15454 this.lastData = false;
15456 this.view.clearSelections();
15462 findRecord : function(prop, value){
15464 if(this.store.getCount() > 0){
15465 this.store.each(function(r){
15466 if(r.data[prop] == value){
15476 getName: function()
15478 // returns hidden if it's set..
15479 if (!this.rendered) {return ''};
15480 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
15484 onViewMove : function(e, t){
15485 this.inKeyMode = false;
15489 onViewOver : function(e, t){
15490 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15493 var item = this.view.findItemFromChild(t);
15496 var index = this.view.indexOf(item);
15497 this.select(index, false);
15502 onViewClick : function(view, doFocus, el, e)
15504 var index = this.view.getSelectedIndexes()[0];
15506 var r = this.store.getAt(index);
15510 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15517 Roo.each(this.tickItems, function(v,k){
15519 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15521 _this.tickItems.splice(k, 1);
15523 if(typeof(e) == 'undefined' && view == false){
15524 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15536 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15537 this.tickItems.push(r.data);
15540 if(typeof(e) == 'undefined' && view == false){
15541 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15548 this.onSelect(r, index);
15550 if(doFocus !== false && !this.blockFocus){
15551 this.inputEl().focus();
15556 restrictHeight : function(){
15557 //this.innerList.dom.style.height = '';
15558 //var inner = this.innerList.dom;
15559 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15560 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15561 //this.list.beginUpdate();
15562 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15563 this.list.alignTo(this.inputEl(), this.listAlign);
15564 this.list.alignTo(this.inputEl(), this.listAlign);
15565 //this.list.endUpdate();
15569 onEmptyResults : function(){
15571 if(this.tickable && this.editable){
15572 this.hasFocus = false;
15573 this.restrictHeight();
15581 * Returns true if the dropdown list is expanded, else false.
15583 isExpanded : function(){
15584 return this.list.isVisible();
15588 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15589 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15590 * @param {String} value The data value of the item to select
15591 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15592 * selected item if it is not currently in view (defaults to true)
15593 * @return {Boolean} True if the value matched an item in the list, else false
15595 selectByValue : function(v, scrollIntoView){
15596 if(v !== undefined && v !== null){
15597 var r = this.findRecord(this.valueField || this.displayField, v);
15599 this.select(this.store.indexOf(r), scrollIntoView);
15607 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15608 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15609 * @param {Number} index The zero-based index of the list item to select
15610 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15611 * selected item if it is not currently in view (defaults to true)
15613 select : function(index, scrollIntoView){
15614 this.selectedIndex = index;
15615 this.view.select(index);
15616 if(scrollIntoView !== false){
15617 var el = this.view.getNode(index);
15619 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15622 this.list.scrollChildIntoView(el, false);
15628 selectNext : function(){
15629 var ct = this.store.getCount();
15631 if(this.selectedIndex == -1){
15633 }else if(this.selectedIndex < ct-1){
15634 this.select(this.selectedIndex+1);
15640 selectPrev : function(){
15641 var ct = this.store.getCount();
15643 if(this.selectedIndex == -1){
15645 }else if(this.selectedIndex != 0){
15646 this.select(this.selectedIndex-1);
15652 onKeyUp : function(e){
15653 if(this.editable !== false && !e.isSpecialKey()){
15654 this.lastKey = e.getKey();
15655 this.dqTask.delay(this.queryDelay);
15660 validateBlur : function(){
15661 return !this.list || !this.list.isVisible();
15665 initQuery : function(){
15667 var v = this.getRawValue();
15669 if(this.tickable && this.editable){
15670 v = this.tickableInputEl().getValue();
15677 doForce : function(){
15678 if(this.inputEl().dom.value.length > 0){
15679 this.inputEl().dom.value =
15680 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15686 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15687 * query allowing the query action to be canceled if needed.
15688 * @param {String} query The SQL query to execute
15689 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15690 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15691 * saved in the current store (defaults to false)
15693 doQuery : function(q, forceAll){
15695 if(q === undefined || q === null){
15700 forceAll: forceAll,
15704 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15709 forceAll = qe.forceAll;
15710 if(forceAll === true || (q.length >= this.minChars)){
15712 this.hasQuery = true;
15714 if(this.lastQuery != q || this.alwaysQuery){
15715 this.lastQuery = q;
15716 if(this.mode == 'local'){
15717 this.selectedIndex = -1;
15719 this.store.clearFilter();
15722 if(this.specialFilter){
15723 this.fireEvent('specialfilter', this);
15728 this.store.filter(this.displayField, q);
15731 this.store.fireEvent("datachanged", this.store);
15738 this.store.baseParams[this.queryParam] = q;
15740 var options = {params : this.getParams(q)};
15743 options.add = true;
15744 options.params.start = this.page * this.pageSize;
15747 this.store.load(options);
15750 * this code will make the page width larger, at the beginning, the list not align correctly,
15751 * we should expand the list on onLoad
15752 * so command out it
15757 this.selectedIndex = -1;
15762 this.loadNext = false;
15766 getParams : function(q){
15768 //p[this.queryParam] = q;
15772 p.limit = this.pageSize;
15778 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15780 collapse : function(){
15781 if(!this.isExpanded()){
15787 this.hasFocus = false;
15791 this.cancelBtn.hide();
15792 this.trigger.show();
15795 this.tickableInputEl().dom.value = '';
15796 this.tickableInputEl().blur();
15801 Roo.get(document).un('mousedown', this.collapseIf, this);
15802 Roo.get(document).un('mousewheel', this.collapseIf, this);
15803 if (!this.editable) {
15804 Roo.get(document).un('keydown', this.listKeyPress, this);
15806 this.fireEvent('collapse', this);
15812 collapseIf : function(e){
15813 var in_combo = e.within(this.el);
15814 var in_list = e.within(this.list);
15815 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15817 if (in_combo || in_list || is_list) {
15818 //e.stopPropagation();
15823 this.onTickableFooterButtonClick(e, false, false);
15831 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15833 expand : function(){
15835 if(this.isExpanded() || !this.hasFocus){
15839 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15840 this.list.setWidth(lw);
15846 this.restrictHeight();
15850 this.tickItems = Roo.apply([], this.item);
15853 this.cancelBtn.show();
15854 this.trigger.hide();
15857 this.tickableInputEl().focus();
15862 Roo.get(document).on('mousedown', this.collapseIf, this);
15863 Roo.get(document).on('mousewheel', this.collapseIf, this);
15864 if (!this.editable) {
15865 Roo.get(document).on('keydown', this.listKeyPress, this);
15868 this.fireEvent('expand', this);
15872 // Implements the default empty TriggerField.onTriggerClick function
15873 onTriggerClick : function(e)
15875 Roo.log('trigger click');
15877 if(this.disabled || !this.triggerList){
15882 this.loadNext = false;
15884 if(this.isExpanded()){
15886 if (!this.blockFocus) {
15887 this.inputEl().focus();
15891 this.hasFocus = true;
15892 if(this.triggerAction == 'all') {
15893 this.doQuery(this.allQuery, true);
15895 this.doQuery(this.getRawValue());
15897 if (!this.blockFocus) {
15898 this.inputEl().focus();
15903 onTickableTriggerClick : function(e)
15910 this.loadNext = false;
15911 this.hasFocus = true;
15913 if(this.triggerAction == 'all') {
15914 this.doQuery(this.allQuery, true);
15916 this.doQuery(this.getRawValue());
15920 onSearchFieldClick : function(e)
15922 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15923 this.onTickableFooterButtonClick(e, false, false);
15927 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15932 this.loadNext = false;
15933 this.hasFocus = true;
15935 if(this.triggerAction == 'all') {
15936 this.doQuery(this.allQuery, true);
15938 this.doQuery(this.getRawValue());
15942 listKeyPress : function(e)
15944 //Roo.log('listkeypress');
15945 // scroll to first matching element based on key pres..
15946 if (e.isSpecialKey()) {
15949 var k = String.fromCharCode(e.getKey()).toUpperCase();
15952 var csel = this.view.getSelectedNodes();
15953 var cselitem = false;
15955 var ix = this.view.indexOf(csel[0]);
15956 cselitem = this.store.getAt(ix);
15957 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15963 this.store.each(function(v) {
15965 // start at existing selection.
15966 if (cselitem.id == v.id) {
15972 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15973 match = this.store.indexOf(v);
15979 if (match === false) {
15980 return true; // no more action?
15983 this.view.select(match);
15984 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15985 sn.scrollIntoView(sn.dom.parentNode, false);
15988 onViewScroll : function(e, t){
15990 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){
15994 this.hasQuery = true;
15996 this.loading = this.list.select('.loading', true).first();
15998 if(this.loading === null){
15999 this.list.createChild({
16001 cls: 'loading roo-select2-more-results roo-select2-active',
16002 html: 'Loading more results...'
16005 this.loading = this.list.select('.loading', true).first();
16007 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16009 this.loading.hide();
16012 this.loading.show();
16017 this.loadNext = true;
16019 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16024 addItem : function(o)
16026 var dv = ''; // display value
16028 if (this.displayField) {
16029 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16031 // this is an error condition!!!
16032 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16039 var choice = this.choices.createChild({
16041 cls: 'roo-select2-search-choice',
16050 cls: 'roo-select2-search-choice-close fa fa-times',
16055 }, this.searchField);
16057 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16059 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16067 this.inputEl().dom.value = '';
16072 onRemoveItem : function(e, _self, o)
16074 e.preventDefault();
16076 this.lastItem = Roo.apply([], this.item);
16078 var index = this.item.indexOf(o.data) * 1;
16081 Roo.log('not this item?!');
16085 this.item.splice(index, 1);
16090 this.fireEvent('remove', this, e);
16096 syncValue : function()
16098 if(!this.item.length){
16105 Roo.each(this.item, function(i){
16106 if(_this.valueField){
16107 value.push(i[_this.valueField]);
16114 this.value = value.join(',');
16116 if(this.hiddenField){
16117 this.hiddenField.dom.value = this.value;
16120 this.store.fireEvent("datachanged", this.store);
16125 clearItem : function()
16127 if(!this.multiple){
16133 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16141 if(this.tickable && !Roo.isTouch){
16142 this.view.refresh();
16146 inputEl: function ()
16148 if(Roo.isIOS && this.useNativeIOS){
16149 return this.el.select('select.roo-ios-select', true).first();
16152 if(Roo.isTouch && this.mobileTouchView){
16153 return this.el.select('input.form-control',true).first();
16157 return this.searchField;
16160 return this.el.select('input.form-control',true).first();
16163 onTickableFooterButtonClick : function(e, btn, el)
16165 e.preventDefault();
16167 this.lastItem = Roo.apply([], this.item);
16169 if(btn && btn.name == 'cancel'){
16170 this.tickItems = Roo.apply([], this.item);
16179 Roo.each(this.tickItems, function(o){
16187 validate : function()
16189 if(this.getVisibilityEl().hasClass('hidden')){
16193 var v = this.getRawValue();
16196 v = this.getValue();
16199 if(this.disabled || this.allowBlank || v.length){
16204 this.markInvalid();
16208 tickableInputEl : function()
16210 if(!this.tickable || !this.editable){
16211 return this.inputEl();
16214 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16218 getAutoCreateTouchView : function()
16223 cls: 'form-group' //input-group
16229 type : this.inputType,
16230 cls : 'form-control x-combo-noedit',
16231 autocomplete: 'new-password',
16232 placeholder : this.placeholder || '',
16237 input.name = this.name;
16241 input.cls += ' input-' + this.size;
16244 if (this.disabled) {
16245 input.disabled = true;
16256 inputblock.cls += ' input-group';
16258 inputblock.cn.unshift({
16260 cls : 'input-group-addon input-group-prepend input-group-text',
16265 if(this.removable && !this.multiple){
16266 inputblock.cls += ' roo-removable';
16268 inputblock.cn.push({
16271 cls : 'roo-combo-removable-btn close'
16275 if(this.hasFeedback && !this.allowBlank){
16277 inputblock.cls += ' has-feedback';
16279 inputblock.cn.push({
16281 cls: 'glyphicon form-control-feedback'
16288 inputblock.cls += (this.before) ? '' : ' input-group';
16290 inputblock.cn.push({
16292 cls : 'input-group-addon input-group-append input-group-text',
16298 var ibwrap = inputblock;
16303 cls: 'roo-select2-choices',
16307 cls: 'roo-select2-search-field',
16320 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16325 cls: 'form-hidden-field'
16331 if(!this.multiple && this.showToggleBtn){
16337 if (this.caret != false) {
16340 cls: 'fa fa-' + this.caret
16347 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16349 Roo.bootstrap.version == 3 ? caret : '',
16352 cls: 'combobox-clear',
16366 combobox.cls += ' roo-select2-container-multi';
16369 var align = this.labelAlign || this.parentLabelAlign();
16371 if (align ==='left' && this.fieldLabel.length) {
16376 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16377 tooltip : 'This field is required'
16381 cls : 'control-label col-form-label',
16382 html : this.fieldLabel
16393 var labelCfg = cfg.cn[1];
16394 var contentCfg = cfg.cn[2];
16397 if(this.indicatorpos == 'right'){
16402 cls : 'control-label col-form-label',
16406 html : this.fieldLabel
16410 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16411 tooltip : 'This field is required'
16424 labelCfg = cfg.cn[0];
16425 contentCfg = cfg.cn[1];
16430 if(this.labelWidth > 12){
16431 labelCfg.style = "width: " + this.labelWidth + 'px';
16434 if(this.labelWidth < 13 && this.labelmd == 0){
16435 this.labelmd = this.labelWidth;
16438 if(this.labellg > 0){
16439 labelCfg.cls += ' col-lg-' + this.labellg;
16440 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16443 if(this.labelmd > 0){
16444 labelCfg.cls += ' col-md-' + this.labelmd;
16445 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16448 if(this.labelsm > 0){
16449 labelCfg.cls += ' col-sm-' + this.labelsm;
16450 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16453 if(this.labelxs > 0){
16454 labelCfg.cls += ' col-xs-' + this.labelxs;
16455 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16459 } else if ( this.fieldLabel.length) {
16463 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16464 tooltip : 'This field is required'
16468 cls : 'control-label',
16469 html : this.fieldLabel
16480 if(this.indicatorpos == 'right'){
16484 cls : 'control-label',
16485 html : this.fieldLabel,
16489 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16490 tooltip : 'This field is required'
16507 var settings = this;
16509 ['xs','sm','md','lg'].map(function(size){
16510 if (settings[size]) {
16511 cfg.cls += ' col-' + size + '-' + settings[size];
16518 initTouchView : function()
16520 this.renderTouchView();
16522 this.touchViewEl.on('scroll', function(){
16523 this.el.dom.scrollTop = 0;
16526 this.originalValue = this.getValue();
16528 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16530 this.inputEl().on("click", this.showTouchView, this);
16531 if (this.triggerEl) {
16532 this.triggerEl.on("click", this.showTouchView, this);
16536 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16537 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16539 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16541 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16542 this.store.on('load', this.onTouchViewLoad, this);
16543 this.store.on('loadexception', this.onTouchViewLoadException, this);
16545 if(this.hiddenName){
16547 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16549 this.hiddenField.dom.value =
16550 this.hiddenValue !== undefined ? this.hiddenValue :
16551 this.value !== undefined ? this.value : '';
16553 this.el.dom.removeAttribute('name');
16554 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16558 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16559 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16562 if(this.removable && !this.multiple){
16563 var close = this.closeTriggerEl();
16565 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16566 close.on('click', this.removeBtnClick, this, close);
16570 * fix the bug in Safari iOS8
16572 this.inputEl().on("focus", function(e){
16573 document.activeElement.blur();
16576 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16583 renderTouchView : function()
16585 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16586 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16588 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16589 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16591 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16592 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16593 this.touchViewBodyEl.setStyle('overflow', 'auto');
16595 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16596 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16598 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16599 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16603 showTouchView : function()
16609 this.touchViewHeaderEl.hide();
16611 if(this.modalTitle.length){
16612 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16613 this.touchViewHeaderEl.show();
16616 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16617 this.touchViewEl.show();
16619 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16621 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16622 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16624 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16626 if(this.modalTitle.length){
16627 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16630 this.touchViewBodyEl.setHeight(bodyHeight);
16634 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16636 this.touchViewEl.addClass('in');
16639 if(this._touchViewMask){
16640 Roo.get(document.body).addClass("x-body-masked");
16641 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16642 this._touchViewMask.setStyle('z-index', 10000);
16643 this._touchViewMask.addClass('show');
16646 this.doTouchViewQuery();
16650 hideTouchView : function()
16652 this.touchViewEl.removeClass('in');
16656 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16658 this.touchViewEl.setStyle('display', 'none');
16661 if(this._touchViewMask){
16662 this._touchViewMask.removeClass('show');
16663 Roo.get(document.body).removeClass("x-body-masked");
16667 setTouchViewValue : function()
16674 Roo.each(this.tickItems, function(o){
16679 this.hideTouchView();
16682 doTouchViewQuery : function()
16691 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16695 if(!this.alwaysQuery || this.mode == 'local'){
16696 this.onTouchViewLoad();
16703 onTouchViewBeforeLoad : function(combo,opts)
16709 onTouchViewLoad : function()
16711 if(this.store.getCount() < 1){
16712 this.onTouchViewEmptyResults();
16716 this.clearTouchView();
16718 var rawValue = this.getRawValue();
16720 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16722 this.tickItems = [];
16724 this.store.data.each(function(d, rowIndex){
16725 var row = this.touchViewListGroup.createChild(template);
16727 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16728 row.addClass(d.data.cls);
16731 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16734 html : d.data[this.displayField]
16737 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16738 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16741 row.removeClass('selected');
16742 if(!this.multiple && this.valueField &&
16743 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16746 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16747 row.addClass('selected');
16750 if(this.multiple && this.valueField &&
16751 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16755 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16756 this.tickItems.push(d.data);
16759 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16763 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16765 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16767 if(this.modalTitle.length){
16768 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16771 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16773 if(this.mobile_restrict_height && listHeight < bodyHeight){
16774 this.touchViewBodyEl.setHeight(listHeight);
16779 if(firstChecked && listHeight > bodyHeight){
16780 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16785 onTouchViewLoadException : function()
16787 this.hideTouchView();
16790 onTouchViewEmptyResults : function()
16792 this.clearTouchView();
16794 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16796 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16800 clearTouchView : function()
16802 this.touchViewListGroup.dom.innerHTML = '';
16805 onTouchViewClick : function(e, el, o)
16807 e.preventDefault();
16810 var rowIndex = o.rowIndex;
16812 var r = this.store.getAt(rowIndex);
16814 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16816 if(!this.multiple){
16817 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16818 c.dom.removeAttribute('checked');
16821 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16823 this.setFromData(r.data);
16825 var close = this.closeTriggerEl();
16831 this.hideTouchView();
16833 this.fireEvent('select', this, r, rowIndex);
16838 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16839 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16840 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16844 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16845 this.addItem(r.data);
16846 this.tickItems.push(r.data);
16850 getAutoCreateNativeIOS : function()
16853 cls: 'form-group' //input-group,
16858 cls : 'roo-ios-select'
16862 combobox.name = this.name;
16865 if (this.disabled) {
16866 combobox.disabled = true;
16869 var settings = this;
16871 ['xs','sm','md','lg'].map(function(size){
16872 if (settings[size]) {
16873 cfg.cls += ' col-' + size + '-' + settings[size];
16883 initIOSView : function()
16885 this.store.on('load', this.onIOSViewLoad, this);
16890 onIOSViewLoad : function()
16892 if(this.store.getCount() < 1){
16896 this.clearIOSView();
16898 if(this.allowBlank) {
16900 var default_text = '-- SELECT --';
16902 if(this.placeholder.length){
16903 default_text = this.placeholder;
16906 if(this.emptyTitle.length){
16907 default_text += ' - ' + this.emptyTitle + ' -';
16910 var opt = this.inputEl().createChild({
16913 html : default_text
16917 o[this.valueField] = 0;
16918 o[this.displayField] = default_text;
16920 this.ios_options.push({
16927 this.store.data.each(function(d, rowIndex){
16931 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16932 html = d.data[this.displayField];
16937 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16938 value = d.data[this.valueField];
16947 if(this.value == d.data[this.valueField]){
16948 option['selected'] = true;
16951 var opt = this.inputEl().createChild(option);
16953 this.ios_options.push({
16960 this.inputEl().on('change', function(){
16961 this.fireEvent('select', this);
16966 clearIOSView: function()
16968 this.inputEl().dom.innerHTML = '';
16970 this.ios_options = [];
16973 setIOSValue: function(v)
16977 if(!this.ios_options){
16981 Roo.each(this.ios_options, function(opts){
16983 opts.el.dom.removeAttribute('selected');
16985 if(opts.data[this.valueField] != v){
16989 opts.el.dom.setAttribute('selected', true);
16995 * @cfg {Boolean} grow
16999 * @cfg {Number} growMin
17003 * @cfg {Number} growMax
17012 Roo.apply(Roo.bootstrap.ComboBox, {
17016 cls: 'modal-header',
17038 cls: 'list-group-item',
17042 cls: 'roo-combobox-list-group-item-value'
17046 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17060 listItemCheckbox : {
17062 cls: 'list-group-item',
17066 cls: 'roo-combobox-list-group-item-value'
17070 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17086 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17091 cls: 'modal-footer',
17099 cls: 'col-xs-6 text-left',
17102 cls: 'btn btn-danger roo-touch-view-cancel',
17108 cls: 'col-xs-6 text-right',
17111 cls: 'btn btn-success roo-touch-view-ok',
17122 Roo.apply(Roo.bootstrap.ComboBox, {
17124 touchViewTemplate : {
17126 cls: 'modal fade roo-combobox-touch-view',
17130 cls: 'modal-dialog',
17131 style : 'position:fixed', // we have to fix position....
17135 cls: 'modal-content',
17137 Roo.bootstrap.ComboBox.header,
17138 Roo.bootstrap.ComboBox.body,
17139 Roo.bootstrap.ComboBox.footer
17148 * Ext JS Library 1.1.1
17149 * Copyright(c) 2006-2007, Ext JS, LLC.
17151 * Originally Released Under LGPL - original licence link has changed is not relivant.
17154 * <script type="text/javascript">
17159 * @extends Roo.util.Observable
17160 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17161 * This class also supports single and multi selection modes. <br>
17162 * Create a data model bound view:
17164 var store = new Roo.data.Store(...);
17166 var view = new Roo.View({
17168 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17170 singleSelect: true,
17171 selectedClass: "ydataview-selected",
17175 // listen for node click?
17176 view.on("click", function(vw, index, node, e){
17177 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17181 dataModel.load("foobar.xml");
17183 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17185 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17186 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17188 * Note: old style constructor is still suported (container, template, config)
17191 * Create a new View
17192 * @param {Object} config The config object
17195 Roo.View = function(config, depreciated_tpl, depreciated_config){
17197 this.parent = false;
17199 if (typeof(depreciated_tpl) == 'undefined') {
17200 // new way.. - universal constructor.
17201 Roo.apply(this, config);
17202 this.el = Roo.get(this.el);
17205 this.el = Roo.get(config);
17206 this.tpl = depreciated_tpl;
17207 Roo.apply(this, depreciated_config);
17209 this.wrapEl = this.el.wrap().wrap();
17210 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17213 if(typeof(this.tpl) == "string"){
17214 this.tpl = new Roo.Template(this.tpl);
17216 // support xtype ctors..
17217 this.tpl = new Roo.factory(this.tpl, Roo);
17221 this.tpl.compile();
17226 * @event beforeclick
17227 * Fires before a click is processed. Returns false to cancel the default action.
17228 * @param {Roo.View} this
17229 * @param {Number} index The index of the target node
17230 * @param {HTMLElement} node The target node
17231 * @param {Roo.EventObject} e The raw event object
17233 "beforeclick" : true,
17236 * Fires when a template node is clicked.
17237 * @param {Roo.View} this
17238 * @param {Number} index The index of the target node
17239 * @param {HTMLElement} node The target node
17240 * @param {Roo.EventObject} e The raw event object
17245 * Fires when a template node is double clicked.
17246 * @param {Roo.View} this
17247 * @param {Number} index The index of the target node
17248 * @param {HTMLElement} node The target node
17249 * @param {Roo.EventObject} e The raw event object
17253 * @event contextmenu
17254 * Fires when a template node is right clicked.
17255 * @param {Roo.View} this
17256 * @param {Number} index The index of the target node
17257 * @param {HTMLElement} node The target node
17258 * @param {Roo.EventObject} e The raw event object
17260 "contextmenu" : true,
17262 * @event selectionchange
17263 * Fires when the selected nodes change.
17264 * @param {Roo.View} this
17265 * @param {Array} selections Array of the selected nodes
17267 "selectionchange" : true,
17270 * @event beforeselect
17271 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17272 * @param {Roo.View} this
17273 * @param {HTMLElement} node The node to be selected
17274 * @param {Array} selections Array of currently selected nodes
17276 "beforeselect" : true,
17278 * @event preparedata
17279 * Fires on every row to render, to allow you to change the data.
17280 * @param {Roo.View} this
17281 * @param {Object} data to be rendered (change this)
17283 "preparedata" : true
17291 "click": this.onClick,
17292 "dblclick": this.onDblClick,
17293 "contextmenu": this.onContextMenu,
17297 this.selections = [];
17299 this.cmp = new Roo.CompositeElementLite([]);
17301 this.store = Roo.factory(this.store, Roo.data);
17302 this.setStore(this.store, true);
17305 if ( this.footer && this.footer.xtype) {
17307 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17309 this.footer.dataSource = this.store;
17310 this.footer.container = fctr;
17311 this.footer = Roo.factory(this.footer, Roo);
17312 fctr.insertFirst(this.el);
17314 // this is a bit insane - as the paging toolbar seems to detach the el..
17315 // dom.parentNode.parentNode.parentNode
17316 // they get detached?
17320 Roo.View.superclass.constructor.call(this);
17325 Roo.extend(Roo.View, Roo.util.Observable, {
17328 * @cfg {Roo.data.Store} store Data store to load data from.
17333 * @cfg {String|Roo.Element} el The container element.
17338 * @cfg {String|Roo.Template} tpl The template used by this View
17342 * @cfg {String} dataName the named area of the template to use as the data area
17343 * Works with domtemplates roo-name="name"
17347 * @cfg {String} selectedClass The css class to add to selected nodes
17349 selectedClass : "x-view-selected",
17351 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17356 * @cfg {String} text to display on mask (default Loading)
17360 * @cfg {Boolean} multiSelect Allow multiple selection
17362 multiSelect : false,
17364 * @cfg {Boolean} singleSelect Allow single selection
17366 singleSelect: false,
17369 * @cfg {Boolean} toggleSelect - selecting
17371 toggleSelect : false,
17374 * @cfg {Boolean} tickable - selecting
17379 * Returns the element this view is bound to.
17380 * @return {Roo.Element}
17382 getEl : function(){
17383 return this.wrapEl;
17389 * Refreshes the view. - called by datachanged on the store. - do not call directly.
17391 refresh : function(){
17392 //Roo.log('refresh');
17395 // if we are using something like 'domtemplate', then
17396 // the what gets used is:
17397 // t.applySubtemplate(NAME, data, wrapping data..)
17398 // the outer template then get' applied with
17399 // the store 'extra data'
17400 // and the body get's added to the
17401 // roo-name="data" node?
17402 // <span class='roo-tpl-{name}'></span> ?????
17406 this.clearSelections();
17407 this.el.update("");
17409 var records = this.store.getRange();
17410 if(records.length < 1) {
17412 // is this valid?? = should it render a template??
17414 this.el.update(this.emptyText);
17418 if (this.dataName) {
17419 this.el.update(t.apply(this.store.meta)); //????
17420 el = this.el.child('.roo-tpl-' + this.dataName);
17423 for(var i = 0, len = records.length; i < len; i++){
17424 var data = this.prepareData(records[i].data, i, records[i]);
17425 this.fireEvent("preparedata", this, data, i, records[i]);
17427 var d = Roo.apply({}, data);
17430 Roo.apply(d, {'roo-id' : Roo.id()});
17434 Roo.each(this.parent.item, function(item){
17435 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17438 Roo.apply(d, {'roo-data-checked' : 'checked'});
17442 html[html.length] = Roo.util.Format.trim(
17444 t.applySubtemplate(this.dataName, d, this.store.meta) :
17451 el.update(html.join(""));
17452 this.nodes = el.dom.childNodes;
17453 this.updateIndexes(0);
17458 * Function to override to reformat the data that is sent to
17459 * the template for each node.
17460 * DEPRICATED - use the preparedata event handler.
17461 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17462 * a JSON object for an UpdateManager bound view).
17464 prepareData : function(data, index, record)
17466 this.fireEvent("preparedata", this, data, index, record);
17470 onUpdate : function(ds, record){
17471 // Roo.log('on update');
17472 this.clearSelections();
17473 var index = this.store.indexOf(record);
17474 var n = this.nodes[index];
17475 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17476 n.parentNode.removeChild(n);
17477 this.updateIndexes(index, index);
17483 onAdd : function(ds, records, index)
17485 //Roo.log(['on Add', ds, records, index] );
17486 this.clearSelections();
17487 if(this.nodes.length == 0){
17491 var n = this.nodes[index];
17492 for(var i = 0, len = records.length; i < len; i++){
17493 var d = this.prepareData(records[i].data, i, records[i]);
17495 this.tpl.insertBefore(n, d);
17498 this.tpl.append(this.el, d);
17501 this.updateIndexes(index);
17504 onRemove : function(ds, record, index){
17505 // Roo.log('onRemove');
17506 this.clearSelections();
17507 var el = this.dataName ?
17508 this.el.child('.roo-tpl-' + this.dataName) :
17511 el.dom.removeChild(this.nodes[index]);
17512 this.updateIndexes(index);
17516 * Refresh an individual node.
17517 * @param {Number} index
17519 refreshNode : function(index){
17520 this.onUpdate(this.store, this.store.getAt(index));
17523 updateIndexes : function(startIndex, endIndex){
17524 var ns = this.nodes;
17525 startIndex = startIndex || 0;
17526 endIndex = endIndex || ns.length - 1;
17527 for(var i = startIndex; i <= endIndex; i++){
17528 ns[i].nodeIndex = i;
17533 * Changes the data store this view uses and refresh the view.
17534 * @param {Store} store
17536 setStore : function(store, initial){
17537 if(!initial && this.store){
17538 this.store.un("datachanged", this.refresh);
17539 this.store.un("add", this.onAdd);
17540 this.store.un("remove", this.onRemove);
17541 this.store.un("update", this.onUpdate);
17542 this.store.un("clear", this.refresh);
17543 this.store.un("beforeload", this.onBeforeLoad);
17544 this.store.un("load", this.onLoad);
17545 this.store.un("loadexception", this.onLoad);
17549 store.on("datachanged", this.refresh, this);
17550 store.on("add", this.onAdd, this);
17551 store.on("remove", this.onRemove, this);
17552 store.on("update", this.onUpdate, this);
17553 store.on("clear", this.refresh, this);
17554 store.on("beforeload", this.onBeforeLoad, this);
17555 store.on("load", this.onLoad, this);
17556 store.on("loadexception", this.onLoad, this);
17564 * onbeforeLoad - masks the loading area.
17567 onBeforeLoad : function(store,opts)
17569 //Roo.log('onBeforeLoad');
17571 this.el.update("");
17573 this.el.mask(this.mask ? this.mask : "Loading" );
17575 onLoad : function ()
17582 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17583 * @param {HTMLElement} node
17584 * @return {HTMLElement} The template node
17586 findItemFromChild : function(node){
17587 var el = this.dataName ?
17588 this.el.child('.roo-tpl-' + this.dataName,true) :
17591 if(!node || node.parentNode == el){
17594 var p = node.parentNode;
17595 while(p && p != el){
17596 if(p.parentNode == el){
17605 onClick : function(e){
17606 var item = this.findItemFromChild(e.getTarget());
17608 var index = this.indexOf(item);
17609 if(this.onItemClick(item, index, e) !== false){
17610 this.fireEvent("click", this, index, item, e);
17613 this.clearSelections();
17618 onContextMenu : function(e){
17619 var item = this.findItemFromChild(e.getTarget());
17621 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17626 onDblClick : function(e){
17627 var item = this.findItemFromChild(e.getTarget());
17629 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17633 onItemClick : function(item, index, e)
17635 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17638 if (this.toggleSelect) {
17639 var m = this.isSelected(item) ? 'unselect' : 'select';
17642 _t[m](item, true, false);
17645 if(this.multiSelect || this.singleSelect){
17646 if(this.multiSelect && e.shiftKey && this.lastSelection){
17647 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17649 this.select(item, this.multiSelect && e.ctrlKey);
17650 this.lastSelection = item;
17653 if(!this.tickable){
17654 e.preventDefault();
17662 * Get the number of selected nodes.
17665 getSelectionCount : function(){
17666 return this.selections.length;
17670 * Get the currently selected nodes.
17671 * @return {Array} An array of HTMLElements
17673 getSelectedNodes : function(){
17674 return this.selections;
17678 * Get the indexes of the selected nodes.
17681 getSelectedIndexes : function(){
17682 var indexes = [], s = this.selections;
17683 for(var i = 0, len = s.length; i < len; i++){
17684 indexes.push(s[i].nodeIndex);
17690 * Clear all selections
17691 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17693 clearSelections : function(suppressEvent){
17694 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17695 this.cmp.elements = this.selections;
17696 this.cmp.removeClass(this.selectedClass);
17697 this.selections = [];
17698 if(!suppressEvent){
17699 this.fireEvent("selectionchange", this, this.selections);
17705 * Returns true if the passed node is selected
17706 * @param {HTMLElement/Number} node The node or node index
17707 * @return {Boolean}
17709 isSelected : function(node){
17710 var s = this.selections;
17714 node = this.getNode(node);
17715 return s.indexOf(node) !== -1;
17720 * @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
17721 * @param {Boolean} keepExisting (optional) true to keep existing selections
17722 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17724 select : function(nodeInfo, keepExisting, suppressEvent){
17725 if(nodeInfo instanceof Array){
17727 this.clearSelections(true);
17729 for(var i = 0, len = nodeInfo.length; i < len; i++){
17730 this.select(nodeInfo[i], true, true);
17734 var node = this.getNode(nodeInfo);
17735 if(!node || this.isSelected(node)){
17736 return; // already selected.
17739 this.clearSelections(true);
17742 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17743 Roo.fly(node).addClass(this.selectedClass);
17744 this.selections.push(node);
17745 if(!suppressEvent){
17746 this.fireEvent("selectionchange", this, this.selections);
17754 * @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
17755 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17756 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17758 unselect : function(nodeInfo, keepExisting, suppressEvent)
17760 if(nodeInfo instanceof Array){
17761 Roo.each(this.selections, function(s) {
17762 this.unselect(s, nodeInfo);
17766 var node = this.getNode(nodeInfo);
17767 if(!node || !this.isSelected(node)){
17768 //Roo.log("not selected");
17769 return; // not selected.
17773 Roo.each(this.selections, function(s) {
17775 Roo.fly(node).removeClass(this.selectedClass);
17782 this.selections= ns;
17783 this.fireEvent("selectionchange", this, this.selections);
17787 * Gets a template node.
17788 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17789 * @return {HTMLElement} The node or null if it wasn't found
17791 getNode : function(nodeInfo){
17792 if(typeof nodeInfo == "string"){
17793 return document.getElementById(nodeInfo);
17794 }else if(typeof nodeInfo == "number"){
17795 return this.nodes[nodeInfo];
17801 * Gets a range template nodes.
17802 * @param {Number} startIndex
17803 * @param {Number} endIndex
17804 * @return {Array} An array of nodes
17806 getNodes : function(start, end){
17807 var ns = this.nodes;
17808 start = start || 0;
17809 end = typeof end == "undefined" ? ns.length - 1 : end;
17812 for(var i = start; i <= end; i++){
17816 for(var i = start; i >= end; i--){
17824 * Finds the index of the passed node
17825 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17826 * @return {Number} The index of the node or -1
17828 indexOf : function(node){
17829 node = this.getNode(node);
17830 if(typeof node.nodeIndex == "number"){
17831 return node.nodeIndex;
17833 var ns = this.nodes;
17834 for(var i = 0, len = ns.length; i < len; i++){
17845 * based on jquery fullcalendar
17849 Roo.bootstrap = Roo.bootstrap || {};
17851 * @class Roo.bootstrap.Calendar
17852 * @extends Roo.bootstrap.Component
17853 * Bootstrap Calendar class
17854 * @cfg {Boolean} loadMask (true|false) default false
17855 * @cfg {Object} header generate the user specific header of the calendar, default false
17858 * Create a new Container
17859 * @param {Object} config The config object
17864 Roo.bootstrap.Calendar = function(config){
17865 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17869 * Fires when a date is selected
17870 * @param {DatePicker} this
17871 * @param {Date} date The selected date
17875 * @event monthchange
17876 * Fires when the displayed month changes
17877 * @param {DatePicker} this
17878 * @param {Date} date The selected month
17880 'monthchange': true,
17882 * @event evententer
17883 * Fires when mouse over an event
17884 * @param {Calendar} this
17885 * @param {event} Event
17887 'evententer': true,
17889 * @event eventleave
17890 * Fires when the mouse leaves an
17891 * @param {Calendar} this
17894 'eventleave': true,
17896 * @event eventclick
17897 * Fires when the mouse click an
17898 * @param {Calendar} this
17907 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17910 * @cfg {Number} startDay
17911 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17919 getAutoCreate : function(){
17922 var fc_button = function(name, corner, style, content ) {
17923 return Roo.apply({},{
17925 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17927 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17930 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17941 style : 'width:100%',
17948 cls : 'fc-header-left',
17950 fc_button('prev', 'left', 'arrow', '‹' ),
17951 fc_button('next', 'right', 'arrow', '›' ),
17952 { tag: 'span', cls: 'fc-header-space' },
17953 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17961 cls : 'fc-header-center',
17965 cls: 'fc-header-title',
17968 html : 'month / year'
17976 cls : 'fc-header-right',
17978 /* fc_button('month', 'left', '', 'month' ),
17979 fc_button('week', '', '', 'week' ),
17980 fc_button('day', 'right', '', 'day' )
17992 header = this.header;
17995 var cal_heads = function() {
17997 // fixme - handle this.
17999 for (var i =0; i < Date.dayNames.length; i++) {
18000 var d = Date.dayNames[i];
18003 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18004 html : d.substring(0,3)
18008 ret[0].cls += ' fc-first';
18009 ret[6].cls += ' fc-last';
18012 var cal_cell = function(n) {
18015 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18020 cls: 'fc-day-number',
18024 cls: 'fc-day-content',
18028 style: 'position: relative;' // height: 17px;
18040 var cal_rows = function() {
18043 for (var r = 0; r < 6; r++) {
18050 for (var i =0; i < Date.dayNames.length; i++) {
18051 var d = Date.dayNames[i];
18052 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18055 row.cn[0].cls+=' fc-first';
18056 row.cn[0].cn[0].style = 'min-height:90px';
18057 row.cn[6].cls+=' fc-last';
18061 ret[0].cls += ' fc-first';
18062 ret[4].cls += ' fc-prev-last';
18063 ret[5].cls += ' fc-last';
18070 cls: 'fc-border-separate',
18071 style : 'width:100%',
18079 cls : 'fc-first fc-last',
18097 cls : 'fc-content',
18098 style : "position: relative;",
18101 cls : 'fc-view fc-view-month fc-grid',
18102 style : 'position: relative',
18103 unselectable : 'on',
18106 cls : 'fc-event-container',
18107 style : 'position:absolute;z-index:8;top:0;left:0;'
18125 initEvents : function()
18128 throw "can not find store for calendar";
18134 style: "text-align:center",
18138 style: "background-color:white;width:50%;margin:250 auto",
18142 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18153 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18155 var size = this.el.select('.fc-content', true).first().getSize();
18156 this.maskEl.setSize(size.width, size.height);
18157 this.maskEl.enableDisplayMode("block");
18158 if(!this.loadMask){
18159 this.maskEl.hide();
18162 this.store = Roo.factory(this.store, Roo.data);
18163 this.store.on('load', this.onLoad, this);
18164 this.store.on('beforeload', this.onBeforeLoad, this);
18168 this.cells = this.el.select('.fc-day',true);
18169 //Roo.log(this.cells);
18170 this.textNodes = this.el.query('.fc-day-number');
18171 this.cells.addClassOnOver('fc-state-hover');
18173 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18174 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18175 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18176 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18178 this.on('monthchange', this.onMonthChange, this);
18180 this.update(new Date().clearTime());
18183 resize : function() {
18184 var sz = this.el.getSize();
18186 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18187 this.el.select('.fc-day-content div',true).setHeight(34);
18192 showPrevMonth : function(e){
18193 this.update(this.activeDate.add("mo", -1));
18195 showToday : function(e){
18196 this.update(new Date().clearTime());
18199 showNextMonth : function(e){
18200 this.update(this.activeDate.add("mo", 1));
18204 showPrevYear : function(){
18205 this.update(this.activeDate.add("y", -1));
18209 showNextYear : function(){
18210 this.update(this.activeDate.add("y", 1));
18215 update : function(date)
18217 var vd = this.activeDate;
18218 this.activeDate = date;
18219 // if(vd && this.el){
18220 // var t = date.getTime();
18221 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18222 // Roo.log('using add remove');
18224 // this.fireEvent('monthchange', this, date);
18226 // this.cells.removeClass("fc-state-highlight");
18227 // this.cells.each(function(c){
18228 // if(c.dateValue == t){
18229 // c.addClass("fc-state-highlight");
18230 // setTimeout(function(){
18231 // try{c.dom.firstChild.focus();}catch(e){}
18241 var days = date.getDaysInMonth();
18243 var firstOfMonth = date.getFirstDateOfMonth();
18244 var startingPos = firstOfMonth.getDay()-this.startDay;
18246 if(startingPos < this.startDay){
18250 var pm = date.add(Date.MONTH, -1);
18251 var prevStart = pm.getDaysInMonth()-startingPos;
18253 this.cells = this.el.select('.fc-day',true);
18254 this.textNodes = this.el.query('.fc-day-number');
18255 this.cells.addClassOnOver('fc-state-hover');
18257 var cells = this.cells.elements;
18258 var textEls = this.textNodes;
18260 Roo.each(cells, function(cell){
18261 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18264 days += startingPos;
18266 // convert everything to numbers so it's fast
18267 var day = 86400000;
18268 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18271 //Roo.log(prevStart);
18273 var today = new Date().clearTime().getTime();
18274 var sel = date.clearTime().getTime();
18275 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18276 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18277 var ddMatch = this.disabledDatesRE;
18278 var ddText = this.disabledDatesText;
18279 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18280 var ddaysText = this.disabledDaysText;
18281 var format = this.format;
18283 var setCellClass = function(cal, cell){
18287 //Roo.log('set Cell Class');
18289 var t = d.getTime();
18293 cell.dateValue = t;
18295 cell.className += " fc-today";
18296 cell.className += " fc-state-highlight";
18297 cell.title = cal.todayText;
18300 // disable highlight in other month..
18301 //cell.className += " fc-state-highlight";
18306 cell.className = " fc-state-disabled";
18307 cell.title = cal.minText;
18311 cell.className = " fc-state-disabled";
18312 cell.title = cal.maxText;
18316 if(ddays.indexOf(d.getDay()) != -1){
18317 cell.title = ddaysText;
18318 cell.className = " fc-state-disabled";
18321 if(ddMatch && format){
18322 var fvalue = d.dateFormat(format);
18323 if(ddMatch.test(fvalue)){
18324 cell.title = ddText.replace("%0", fvalue);
18325 cell.className = " fc-state-disabled";
18329 if (!cell.initialClassName) {
18330 cell.initialClassName = cell.dom.className;
18333 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18338 for(; i < startingPos; i++) {
18339 textEls[i].innerHTML = (++prevStart);
18340 d.setDate(d.getDate()+1);
18342 cells[i].className = "fc-past fc-other-month";
18343 setCellClass(this, cells[i]);
18348 for(; i < days; i++){
18349 intDay = i - startingPos + 1;
18350 textEls[i].innerHTML = (intDay);
18351 d.setDate(d.getDate()+1);
18353 cells[i].className = ''; // "x-date-active";
18354 setCellClass(this, cells[i]);
18358 for(; i < 42; i++) {
18359 textEls[i].innerHTML = (++extraDays);
18360 d.setDate(d.getDate()+1);
18362 cells[i].className = "fc-future fc-other-month";
18363 setCellClass(this, cells[i]);
18366 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18368 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18370 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18371 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18373 if(totalRows != 6){
18374 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18375 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18378 this.fireEvent('monthchange', this, date);
18382 if(!this.internalRender){
18383 var main = this.el.dom.firstChild;
18384 var w = main.offsetWidth;
18385 this.el.setWidth(w + this.el.getBorderWidth("lr"));
18386 Roo.fly(main).setWidth(w);
18387 this.internalRender = true;
18388 // opera does not respect the auto grow header center column
18389 // then, after it gets a width opera refuses to recalculate
18390 // without a second pass
18391 if(Roo.isOpera && !this.secondPass){
18392 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18393 this.secondPass = true;
18394 this.update.defer(10, this, [date]);
18401 findCell : function(dt) {
18402 dt = dt.clearTime().getTime();
18404 this.cells.each(function(c){
18405 //Roo.log("check " +c.dateValue + '?=' + dt);
18406 if(c.dateValue == dt){
18416 findCells : function(ev) {
18417 var s = ev.start.clone().clearTime().getTime();
18419 var e= ev.end.clone().clearTime().getTime();
18422 this.cells.each(function(c){
18423 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18425 if(c.dateValue > e){
18428 if(c.dateValue < s){
18437 // findBestRow: function(cells)
18441 // for (var i =0 ; i < cells.length;i++) {
18442 // ret = Math.max(cells[i].rows || 0,ret);
18449 addItem : function(ev)
18451 // look for vertical location slot in
18452 var cells = this.findCells(ev);
18454 // ev.row = this.findBestRow(cells);
18456 // work out the location.
18460 for(var i =0; i < cells.length; i++) {
18462 cells[i].row = cells[0].row;
18465 cells[i].row = cells[i].row + 1;
18475 if (crow.start.getY() == cells[i].getY()) {
18477 crow.end = cells[i];
18494 cells[0].events.push(ev);
18496 this.calevents.push(ev);
18499 clearEvents: function() {
18501 if(!this.calevents){
18505 Roo.each(this.cells.elements, function(c){
18511 Roo.each(this.calevents, function(e) {
18512 Roo.each(e.els, function(el) {
18513 el.un('mouseenter' ,this.onEventEnter, this);
18514 el.un('mouseleave' ,this.onEventLeave, this);
18519 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18525 renderEvents: function()
18529 this.cells.each(function(c) {
18538 if(c.row != c.events.length){
18539 r = 4 - (4 - (c.row - c.events.length));
18542 c.events = ev.slice(0, r);
18543 c.more = ev.slice(r);
18545 if(c.more.length && c.more.length == 1){
18546 c.events.push(c.more.pop());
18549 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18553 this.cells.each(function(c) {
18555 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18558 for (var e = 0; e < c.events.length; e++){
18559 var ev = c.events[e];
18560 var rows = ev.rows;
18562 for(var i = 0; i < rows.length; i++) {
18564 // how many rows should it span..
18567 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18568 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18570 unselectable : "on",
18573 cls: 'fc-event-inner',
18577 // cls: 'fc-event-time',
18578 // html : cells.length > 1 ? '' : ev.time
18582 cls: 'fc-event-title',
18583 html : String.format('{0}', ev.title)
18590 cls: 'ui-resizable-handle ui-resizable-e',
18591 html : '  '
18598 cfg.cls += ' fc-event-start';
18600 if ((i+1) == rows.length) {
18601 cfg.cls += ' fc-event-end';
18604 var ctr = _this.el.select('.fc-event-container',true).first();
18605 var cg = ctr.createChild(cfg);
18607 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18608 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18610 var r = (c.more.length) ? 1 : 0;
18611 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18612 cg.setWidth(ebox.right - sbox.x -2);
18614 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18615 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18616 cg.on('click', _this.onEventClick, _this, ev);
18627 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18628 style : 'position: absolute',
18629 unselectable : "on",
18632 cls: 'fc-event-inner',
18636 cls: 'fc-event-title',
18644 cls: 'ui-resizable-handle ui-resizable-e',
18645 html : '  '
18651 var ctr = _this.el.select('.fc-event-container',true).first();
18652 var cg = ctr.createChild(cfg);
18654 var sbox = c.select('.fc-day-content',true).first().getBox();
18655 var ebox = c.select('.fc-day-content',true).first().getBox();
18657 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18658 cg.setWidth(ebox.right - sbox.x -2);
18660 cg.on('click', _this.onMoreEventClick, _this, c.more);
18670 onEventEnter: function (e, el,event,d) {
18671 this.fireEvent('evententer', this, el, event);
18674 onEventLeave: function (e, el,event,d) {
18675 this.fireEvent('eventleave', this, el, event);
18678 onEventClick: function (e, el,event,d) {
18679 this.fireEvent('eventclick', this, el, event);
18682 onMonthChange: function () {
18686 onMoreEventClick: function(e, el, more)
18690 this.calpopover.placement = 'right';
18691 this.calpopover.setTitle('More');
18693 this.calpopover.setContent('');
18695 var ctr = this.calpopover.el.select('.popover-content', true).first();
18697 Roo.each(more, function(m){
18699 cls : 'fc-event-hori fc-event-draggable',
18702 var cg = ctr.createChild(cfg);
18704 cg.on('click', _this.onEventClick, _this, m);
18707 this.calpopover.show(el);
18712 onLoad: function ()
18714 this.calevents = [];
18717 if(this.store.getCount() > 0){
18718 this.store.data.each(function(d){
18721 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18722 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18723 time : d.data.start_time,
18724 title : d.data.title,
18725 description : d.data.description,
18726 venue : d.data.venue
18731 this.renderEvents();
18733 if(this.calevents.length && this.loadMask){
18734 this.maskEl.hide();
18738 onBeforeLoad: function()
18740 this.clearEvents();
18742 this.maskEl.show();
18756 * @class Roo.bootstrap.Popover
18757 * @extends Roo.bootstrap.Component
18758 * Bootstrap Popover class
18759 * @cfg {String} html contents of the popover (or false to use children..)
18760 * @cfg {String} title of popover (or false to hide)
18761 * @cfg {String} placement how it is placed
18762 * @cfg {String} trigger click || hover (or false to trigger manually)
18763 * @cfg {String} over what (parent or false to trigger manually.)
18764 * @cfg {Number} delay - delay before showing
18767 * Create a new Popover
18768 * @param {Object} config The config object
18771 Roo.bootstrap.Popover = function(config){
18772 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18778 * After the popover show
18780 * @param {Roo.bootstrap.Popover} this
18785 * After the popover hide
18787 * @param {Roo.bootstrap.Popover} this
18793 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18795 title: 'Fill in a title',
18798 placement : 'right',
18799 trigger : 'hover', // hover
18805 can_build_overlaid : false,
18807 getChildContainer : function()
18809 return this.el.select('.popover-content',true).first();
18812 getAutoCreate : function(){
18815 cls : 'popover roo-dynamic',
18816 style: 'display:block',
18822 cls : 'popover-inner',
18826 cls: 'popover-title popover-header',
18830 cls : 'popover-content popover-body',
18841 setTitle: function(str)
18844 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18846 setContent: function(str)
18849 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18851 // as it get's added to the bottom of the page.
18852 onRender : function(ct, position)
18854 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18856 var cfg = Roo.apply({}, this.getAutoCreate());
18860 cfg.cls += ' ' + this.cls;
18863 cfg.style = this.style;
18865 //Roo.log("adding to ");
18866 this.el = Roo.get(document.body).createChild(cfg, position);
18867 // Roo.log(this.el);
18872 initEvents : function()
18874 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18875 this.el.enableDisplayMode('block');
18877 if (this.over === false) {
18880 if (this.triggers === false) {
18883 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18884 var triggers = this.trigger ? this.trigger.split(' ') : [];
18885 Roo.each(triggers, function(trigger) {
18887 if (trigger == 'click') {
18888 on_el.on('click', this.toggle, this);
18889 } else if (trigger != 'manual') {
18890 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18891 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18893 on_el.on(eventIn ,this.enter, this);
18894 on_el.on(eventOut, this.leave, this);
18905 toggle : function () {
18906 this.hoverState == 'in' ? this.leave() : this.enter();
18909 enter : function () {
18911 clearTimeout(this.timeout);
18913 this.hoverState = 'in';
18915 if (!this.delay || !this.delay.show) {
18920 this.timeout = setTimeout(function () {
18921 if (_t.hoverState == 'in') {
18924 }, this.delay.show)
18927 leave : function() {
18928 clearTimeout(this.timeout);
18930 this.hoverState = 'out';
18932 if (!this.delay || !this.delay.hide) {
18937 this.timeout = setTimeout(function () {
18938 if (_t.hoverState == 'out') {
18941 }, this.delay.hide)
18944 show : function (on_el)
18947 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18951 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18952 if (this.html !== false) {
18953 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18955 this.el.removeClass([
18956 'fade','top','bottom', 'left', 'right','in',
18957 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18959 if (!this.title.length) {
18960 this.el.select('.popover-title',true).hide();
18963 var placement = typeof this.placement == 'function' ?
18964 this.placement.call(this, this.el, on_el) :
18967 var autoToken = /\s?auto?\s?/i;
18968 var autoPlace = autoToken.test(placement);
18970 placement = placement.replace(autoToken, '') || 'top';
18974 //this.el.setXY([0,0]);
18976 this.el.dom.style.display='block';
18977 this.el.addClass(placement);
18979 //this.el.appendTo(on_el);
18981 var p = this.getPosition();
18982 var box = this.el.getBox();
18987 var align = Roo.bootstrap.Popover.alignment[placement];
18990 this.el.alignTo(on_el, align[0],align[1]);
18991 //var arrow = this.el.select('.arrow',true).first();
18992 //arrow.set(align[2],
18994 this.el.addClass('in');
18997 if (this.el.hasClass('fade')) {
19001 this.hoverState = 'in';
19003 this.fireEvent('show', this);
19008 this.el.setXY([0,0]);
19009 this.el.removeClass('in');
19011 this.hoverState = null;
19013 this.fireEvent('hide', this);
19018 Roo.bootstrap.Popover.alignment = {
19019 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19020 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19021 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19022 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19033 * @class Roo.bootstrap.Progress
19034 * @extends Roo.bootstrap.Component
19035 * Bootstrap Progress class
19036 * @cfg {Boolean} striped striped of the progress bar
19037 * @cfg {Boolean} active animated of the progress bar
19041 * Create a new Progress
19042 * @param {Object} config The config object
19045 Roo.bootstrap.Progress = function(config){
19046 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19049 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19054 getAutoCreate : function(){
19062 cfg.cls += ' progress-striped';
19066 cfg.cls += ' active';
19085 * @class Roo.bootstrap.ProgressBar
19086 * @extends Roo.bootstrap.Component
19087 * Bootstrap ProgressBar class
19088 * @cfg {Number} aria_valuenow aria-value now
19089 * @cfg {Number} aria_valuemin aria-value min
19090 * @cfg {Number} aria_valuemax aria-value max
19091 * @cfg {String} label label for the progress bar
19092 * @cfg {String} panel (success | info | warning | danger )
19093 * @cfg {String} role role of the progress bar
19094 * @cfg {String} sr_only text
19098 * Create a new ProgressBar
19099 * @param {Object} config The config object
19102 Roo.bootstrap.ProgressBar = function(config){
19103 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19106 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19110 aria_valuemax : 100,
19116 getAutoCreate : function()
19121 cls: 'progress-bar',
19122 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19134 cfg.role = this.role;
19137 if(this.aria_valuenow){
19138 cfg['aria-valuenow'] = this.aria_valuenow;
19141 if(this.aria_valuemin){
19142 cfg['aria-valuemin'] = this.aria_valuemin;
19145 if(this.aria_valuemax){
19146 cfg['aria-valuemax'] = this.aria_valuemax;
19149 if(this.label && !this.sr_only){
19150 cfg.html = this.label;
19154 cfg.cls += ' progress-bar-' + this.panel;
19160 update : function(aria_valuenow)
19162 this.aria_valuenow = aria_valuenow;
19164 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19179 * @class Roo.bootstrap.TabGroup
19180 * @extends Roo.bootstrap.Column
19181 * Bootstrap Column class
19182 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19183 * @cfg {Boolean} carousel true to make the group behave like a carousel
19184 * @cfg {Boolean} bullets show bullets for the panels
19185 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19186 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19187 * @cfg {Boolean} showarrow (true|false) show arrow default true
19190 * Create a new TabGroup
19191 * @param {Object} config The config object
19194 Roo.bootstrap.TabGroup = function(config){
19195 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19197 this.navId = Roo.id();
19200 Roo.bootstrap.TabGroup.register(this);
19204 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19207 transition : false,
19212 slideOnTouch : false,
19215 getAutoCreate : function()
19217 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19219 cfg.cls += ' tab-content';
19221 if (this.carousel) {
19222 cfg.cls += ' carousel slide';
19225 cls : 'carousel-inner',
19229 if(this.bullets && !Roo.isTouch){
19232 cls : 'carousel-bullets',
19236 if(this.bullets_cls){
19237 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19244 cfg.cn[0].cn.push(bullets);
19247 if(this.showarrow){
19248 cfg.cn[0].cn.push({
19250 class : 'carousel-arrow',
19254 class : 'carousel-prev',
19258 class : 'fa fa-chevron-left'
19264 class : 'carousel-next',
19268 class : 'fa fa-chevron-right'
19281 initEvents: function()
19283 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19284 // this.el.on("touchstart", this.onTouchStart, this);
19287 if(this.autoslide){
19290 this.slideFn = window.setInterval(function() {
19291 _this.showPanelNext();
19295 if(this.showarrow){
19296 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19297 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19303 // onTouchStart : function(e, el, o)
19305 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19309 // this.showPanelNext();
19313 getChildContainer : function()
19315 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19319 * register a Navigation item
19320 * @param {Roo.bootstrap.NavItem} the navitem to add
19322 register : function(item)
19324 this.tabs.push( item);
19325 item.navId = this.navId; // not really needed..
19330 getActivePanel : function()
19333 Roo.each(this.tabs, function(t) {
19343 getPanelByName : function(n)
19346 Roo.each(this.tabs, function(t) {
19347 if (t.tabId == n) {
19355 indexOfPanel : function(p)
19358 Roo.each(this.tabs, function(t,i) {
19359 if (t.tabId == p.tabId) {
19368 * show a specific panel
19369 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19370 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19372 showPanel : function (pan)
19374 if(this.transition || typeof(pan) == 'undefined'){
19375 Roo.log("waiting for the transitionend");
19379 if (typeof(pan) == 'number') {
19380 pan = this.tabs[pan];
19383 if (typeof(pan) == 'string') {
19384 pan = this.getPanelByName(pan);
19387 var cur = this.getActivePanel();
19390 Roo.log('pan or acitve pan is undefined');
19394 if (pan.tabId == this.getActivePanel().tabId) {
19398 if (false === cur.fireEvent('beforedeactivate')) {
19402 if(this.bullets > 0 && !Roo.isTouch){
19403 this.setActiveBullet(this.indexOfPanel(pan));
19406 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19408 //class="carousel-item carousel-item-next carousel-item-left"
19410 this.transition = true;
19411 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
19412 var lr = dir == 'next' ? 'left' : 'right';
19413 pan.el.addClass(dir); // or prev
19414 pan.el.addClass('carousel-item-' + dir); // or prev
19415 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19416 cur.el.addClass(lr); // or right
19417 pan.el.addClass(lr);
19418 cur.el.addClass('carousel-item-' +lr); // or right
19419 pan.el.addClass('carousel-item-' +lr);
19423 cur.el.on('transitionend', function() {
19424 Roo.log("trans end?");
19426 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19427 pan.setActive(true);
19429 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19430 cur.setActive(false);
19432 _this.transition = false;
19434 }, this, { single: true } );
19439 cur.setActive(false);
19440 pan.setActive(true);
19445 showPanelNext : function()
19447 var i = this.indexOfPanel(this.getActivePanel());
19449 if (i >= this.tabs.length - 1 && !this.autoslide) {
19453 if (i >= this.tabs.length - 1 && this.autoslide) {
19457 this.showPanel(this.tabs[i+1]);
19460 showPanelPrev : function()
19462 var i = this.indexOfPanel(this.getActivePanel());
19464 if (i < 1 && !this.autoslide) {
19468 if (i < 1 && this.autoslide) {
19469 i = this.tabs.length;
19472 this.showPanel(this.tabs[i-1]);
19476 addBullet: function()
19478 if(!this.bullets || Roo.isTouch){
19481 var ctr = this.el.select('.carousel-bullets',true).first();
19482 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19483 var bullet = ctr.createChild({
19484 cls : 'bullet bullet-' + i
19485 },ctr.dom.lastChild);
19490 bullet.on('click', (function(e, el, o, ii, t){
19492 e.preventDefault();
19494 this.showPanel(ii);
19496 if(this.autoslide && this.slideFn){
19497 clearInterval(this.slideFn);
19498 this.slideFn = window.setInterval(function() {
19499 _this.showPanelNext();
19503 }).createDelegate(this, [i, bullet], true));
19508 setActiveBullet : function(i)
19514 Roo.each(this.el.select('.bullet', true).elements, function(el){
19515 el.removeClass('selected');
19518 var bullet = this.el.select('.bullet-' + i, true).first();
19524 bullet.addClass('selected');
19535 Roo.apply(Roo.bootstrap.TabGroup, {
19539 * register a Navigation Group
19540 * @param {Roo.bootstrap.NavGroup} the navgroup to add
19542 register : function(navgrp)
19544 this.groups[navgrp.navId] = navgrp;
19548 * fetch a Navigation Group based on the navigation ID
19549 * if one does not exist , it will get created.
19550 * @param {string} the navgroup to add
19551 * @returns {Roo.bootstrap.NavGroup} the navgroup
19553 get: function(navId) {
19554 if (typeof(this.groups[navId]) == 'undefined') {
19555 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19557 return this.groups[navId] ;
19572 * @class Roo.bootstrap.TabPanel
19573 * @extends Roo.bootstrap.Component
19574 * Bootstrap TabPanel class
19575 * @cfg {Boolean} active panel active
19576 * @cfg {String} html panel content
19577 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19578 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19579 * @cfg {String} href click to link..
19583 * Create a new TabPanel
19584 * @param {Object} config The config object
19587 Roo.bootstrap.TabPanel = function(config){
19588 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19592 * Fires when the active status changes
19593 * @param {Roo.bootstrap.TabPanel} this
19594 * @param {Boolean} state the new state
19599 * @event beforedeactivate
19600 * Fires before a tab is de-activated - can be used to do validation on a form.
19601 * @param {Roo.bootstrap.TabPanel} this
19602 * @return {Boolean} false if there is an error
19605 'beforedeactivate': true
19608 this.tabId = this.tabId || Roo.id();
19612 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19620 getAutoCreate : function(){
19625 // item is needed for carousel - not sure if it has any effect otherwise
19626 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19627 html: this.html || ''
19631 cfg.cls += ' active';
19635 cfg.tabId = this.tabId;
19643 initEvents: function()
19645 var p = this.parent();
19647 this.navId = this.navId || p.navId;
19649 if (typeof(this.navId) != 'undefined') {
19650 // not really needed.. but just in case.. parent should be a NavGroup.
19651 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19655 var i = tg.tabs.length - 1;
19657 if(this.active && tg.bullets > 0 && i < tg.bullets){
19658 tg.setActiveBullet(i);
19662 this.el.on('click', this.onClick, this);
19665 this.el.on("touchstart", this.onTouchStart, this);
19666 this.el.on("touchmove", this.onTouchMove, this);
19667 this.el.on("touchend", this.onTouchEnd, this);
19672 onRender : function(ct, position)
19674 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19677 setActive : function(state)
19679 Roo.log("panel - set active " + this.tabId + "=" + state);
19681 this.active = state;
19683 this.el.removeClass('active');
19685 } else if (!this.el.hasClass('active')) {
19686 this.el.addClass('active');
19689 this.fireEvent('changed', this, state);
19692 onClick : function(e)
19694 e.preventDefault();
19696 if(!this.href.length){
19700 window.location.href = this.href;
19709 onTouchStart : function(e)
19711 this.swiping = false;
19713 this.startX = e.browserEvent.touches[0].clientX;
19714 this.startY = e.browserEvent.touches[0].clientY;
19717 onTouchMove : function(e)
19719 this.swiping = true;
19721 this.endX = e.browserEvent.touches[0].clientX;
19722 this.endY = e.browserEvent.touches[0].clientY;
19725 onTouchEnd : function(e)
19732 var tabGroup = this.parent();
19734 if(this.endX > this.startX){ // swiping right
19735 tabGroup.showPanelPrev();
19739 if(this.startX > this.endX){ // swiping left
19740 tabGroup.showPanelNext();
19759 * @class Roo.bootstrap.DateField
19760 * @extends Roo.bootstrap.Input
19761 * Bootstrap DateField class
19762 * @cfg {Number} weekStart default 0
19763 * @cfg {String} viewMode default empty, (months|years)
19764 * @cfg {String} minViewMode default empty, (months|years)
19765 * @cfg {Number} startDate default -Infinity
19766 * @cfg {Number} endDate default Infinity
19767 * @cfg {Boolean} todayHighlight default false
19768 * @cfg {Boolean} todayBtn default false
19769 * @cfg {Boolean} calendarWeeks default false
19770 * @cfg {Object} daysOfWeekDisabled default empty
19771 * @cfg {Boolean} singleMode default false (true | false)
19773 * @cfg {Boolean} keyboardNavigation default true
19774 * @cfg {String} language default en
19777 * Create a new DateField
19778 * @param {Object} config The config object
19781 Roo.bootstrap.DateField = function(config){
19782 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19786 * Fires when this field show.
19787 * @param {Roo.bootstrap.DateField} this
19788 * @param {Mixed} date The date value
19793 * Fires when this field hide.
19794 * @param {Roo.bootstrap.DateField} this
19795 * @param {Mixed} date The date value
19800 * Fires when select a date.
19801 * @param {Roo.bootstrap.DateField} this
19802 * @param {Mixed} date The date value
19806 * @event beforeselect
19807 * Fires when before select a date.
19808 * @param {Roo.bootstrap.DateField} this
19809 * @param {Mixed} date The date value
19811 beforeselect : true
19815 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19818 * @cfg {String} format
19819 * The default date format string which can be overriden for localization support. The format must be
19820 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19824 * @cfg {String} altFormats
19825 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19826 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19828 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19836 todayHighlight : false,
19842 keyboardNavigation: true,
19844 calendarWeeks: false,
19846 startDate: -Infinity,
19850 daysOfWeekDisabled: [],
19854 singleMode : false,
19856 UTCDate: function()
19858 return new Date(Date.UTC.apply(Date, arguments));
19861 UTCToday: function()
19863 var today = new Date();
19864 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19867 getDate: function() {
19868 var d = this.getUTCDate();
19869 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19872 getUTCDate: function() {
19876 setDate: function(d) {
19877 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19880 setUTCDate: function(d) {
19882 this.setValue(this.formatDate(this.date));
19885 onRender: function(ct, position)
19888 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19890 this.language = this.language || 'en';
19891 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19892 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19894 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19895 this.format = this.format || 'm/d/y';
19896 this.isInline = false;
19897 this.isInput = true;
19898 this.component = this.el.select('.add-on', true).first() || false;
19899 this.component = (this.component && this.component.length === 0) ? false : this.component;
19900 this.hasInput = this.component && this.inputEl().length;
19902 if (typeof(this.minViewMode === 'string')) {
19903 switch (this.minViewMode) {
19905 this.minViewMode = 1;
19908 this.minViewMode = 2;
19911 this.minViewMode = 0;
19916 if (typeof(this.viewMode === 'string')) {
19917 switch (this.viewMode) {
19930 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19932 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19934 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19936 this.picker().on('mousedown', this.onMousedown, this);
19937 this.picker().on('click', this.onClick, this);
19939 this.picker().addClass('datepicker-dropdown');
19941 this.startViewMode = this.viewMode;
19943 if(this.singleMode){
19944 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19945 v.setVisibilityMode(Roo.Element.DISPLAY);
19949 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19950 v.setStyle('width', '189px');
19954 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19955 if(!this.calendarWeeks){
19960 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19961 v.attr('colspan', function(i, val){
19962 return parseInt(val) + 1;
19967 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19969 this.setStartDate(this.startDate);
19970 this.setEndDate(this.endDate);
19972 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19979 if(this.isInline) {
19984 picker : function()
19986 return this.pickerEl;
19987 // return this.el.select('.datepicker', true).first();
19990 fillDow: function()
19992 var dowCnt = this.weekStart;
20001 if(this.calendarWeeks){
20009 while (dowCnt < this.weekStart + 7) {
20013 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20017 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20020 fillMonths: function()
20023 var months = this.picker().select('>.datepicker-months td', true).first();
20025 months.dom.innerHTML = '';
20031 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20034 months.createChild(month);
20041 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;
20043 if (this.date < this.startDate) {
20044 this.viewDate = new Date(this.startDate);
20045 } else if (this.date > this.endDate) {
20046 this.viewDate = new Date(this.endDate);
20048 this.viewDate = new Date(this.date);
20056 var d = new Date(this.viewDate),
20057 year = d.getUTCFullYear(),
20058 month = d.getUTCMonth(),
20059 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20060 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20061 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20062 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20063 currentDate = this.date && this.date.valueOf(),
20064 today = this.UTCToday();
20066 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20068 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20070 // this.picker.select('>tfoot th.today').
20071 // .text(dates[this.language].today)
20072 // .toggle(this.todayBtn !== false);
20074 this.updateNavArrows();
20077 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20079 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20081 prevMonth.setUTCDate(day);
20083 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20085 var nextMonth = new Date(prevMonth);
20087 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20089 nextMonth = nextMonth.valueOf();
20091 var fillMonths = false;
20093 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20095 while(prevMonth.valueOf() <= nextMonth) {
20098 if (prevMonth.getUTCDay() === this.weekStart) {
20100 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20108 if(this.calendarWeeks){
20109 // ISO 8601: First week contains first thursday.
20110 // ISO also states week starts on Monday, but we can be more abstract here.
20112 // Start of current week: based on weekstart/current date
20113 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20114 // Thursday of this week
20115 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20116 // First Thursday of year, year from thursday
20117 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20118 // Calendar week: ms between thursdays, div ms per day, div 7 days
20119 calWeek = (th - yth) / 864e5 / 7 + 1;
20121 fillMonths.cn.push({
20129 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20131 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20134 if (this.todayHighlight &&
20135 prevMonth.getUTCFullYear() == today.getFullYear() &&
20136 prevMonth.getUTCMonth() == today.getMonth() &&
20137 prevMonth.getUTCDate() == today.getDate()) {
20138 clsName += ' today';
20141 if (currentDate && prevMonth.valueOf() === currentDate) {
20142 clsName += ' active';
20145 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20146 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20147 clsName += ' disabled';
20150 fillMonths.cn.push({
20152 cls: 'day ' + clsName,
20153 html: prevMonth.getDate()
20156 prevMonth.setDate(prevMonth.getDate()+1);
20159 var currentYear = this.date && this.date.getUTCFullYear();
20160 var currentMonth = this.date && this.date.getUTCMonth();
20162 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20164 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20165 v.removeClass('active');
20167 if(currentYear === year && k === currentMonth){
20168 v.addClass('active');
20171 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20172 v.addClass('disabled');
20178 year = parseInt(year/10, 10) * 10;
20180 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20182 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20185 for (var i = -1; i < 11; i++) {
20186 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20188 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20196 showMode: function(dir)
20199 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20202 Roo.each(this.picker().select('>div',true).elements, function(v){
20203 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20206 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20211 if(this.isInline) {
20215 this.picker().removeClass(['bottom', 'top']);
20217 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20219 * place to the top of element!
20223 this.picker().addClass('top');
20224 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20229 this.picker().addClass('bottom');
20231 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20234 parseDate : function(value)
20236 if(!value || value instanceof Date){
20239 var v = Date.parseDate(value, this.format);
20240 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20241 v = Date.parseDate(value, 'Y-m-d');
20243 if(!v && this.altFormats){
20244 if(!this.altFormatsArray){
20245 this.altFormatsArray = this.altFormats.split("|");
20247 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20248 v = Date.parseDate(value, this.altFormatsArray[i]);
20254 formatDate : function(date, fmt)
20256 return (!date || !(date instanceof Date)) ?
20257 date : date.dateFormat(fmt || this.format);
20260 onFocus : function()
20262 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20266 onBlur : function()
20268 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20270 var d = this.inputEl().getValue();
20277 showPopup : function()
20279 this.picker().show();
20283 this.fireEvent('showpopup', this, this.date);
20286 hidePopup : function()
20288 if(this.isInline) {
20291 this.picker().hide();
20292 this.viewMode = this.startViewMode;
20295 this.fireEvent('hidepopup', this, this.date);
20299 onMousedown: function(e)
20301 e.stopPropagation();
20302 e.preventDefault();
20307 Roo.bootstrap.DateField.superclass.keyup.call(this);
20311 setValue: function(v)
20313 if(this.fireEvent('beforeselect', this, v) !== false){
20314 var d = new Date(this.parseDate(v) ).clearTime();
20316 if(isNaN(d.getTime())){
20317 this.date = this.viewDate = '';
20318 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20322 v = this.formatDate(d);
20324 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20326 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20330 this.fireEvent('select', this, this.date);
20334 getValue: function()
20336 return this.formatDate(this.date);
20339 fireKey: function(e)
20341 if (!this.picker().isVisible()){
20342 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20348 var dateChanged = false,
20350 newDate, newViewDate;
20355 e.preventDefault();
20359 if (!this.keyboardNavigation) {
20362 dir = e.keyCode == 37 ? -1 : 1;
20365 newDate = this.moveYear(this.date, dir);
20366 newViewDate = this.moveYear(this.viewDate, dir);
20367 } else if (e.shiftKey){
20368 newDate = this.moveMonth(this.date, dir);
20369 newViewDate = this.moveMonth(this.viewDate, dir);
20371 newDate = new Date(this.date);
20372 newDate.setUTCDate(this.date.getUTCDate() + dir);
20373 newViewDate = new Date(this.viewDate);
20374 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20376 if (this.dateWithinRange(newDate)){
20377 this.date = newDate;
20378 this.viewDate = newViewDate;
20379 this.setValue(this.formatDate(this.date));
20381 e.preventDefault();
20382 dateChanged = true;
20387 if (!this.keyboardNavigation) {
20390 dir = e.keyCode == 38 ? -1 : 1;
20392 newDate = this.moveYear(this.date, dir);
20393 newViewDate = this.moveYear(this.viewDate, dir);
20394 } else if (e.shiftKey){
20395 newDate = this.moveMonth(this.date, dir);
20396 newViewDate = this.moveMonth(this.viewDate, dir);
20398 newDate = new Date(this.date);
20399 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20400 newViewDate = new Date(this.viewDate);
20401 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20403 if (this.dateWithinRange(newDate)){
20404 this.date = newDate;
20405 this.viewDate = newViewDate;
20406 this.setValue(this.formatDate(this.date));
20408 e.preventDefault();
20409 dateChanged = true;
20413 this.setValue(this.formatDate(this.date));
20415 e.preventDefault();
20418 this.setValue(this.formatDate(this.date));
20432 onClick: function(e)
20434 e.stopPropagation();
20435 e.preventDefault();
20437 var target = e.getTarget();
20439 if(target.nodeName.toLowerCase() === 'i'){
20440 target = Roo.get(target).dom.parentNode;
20443 var nodeName = target.nodeName;
20444 var className = target.className;
20445 var html = target.innerHTML;
20446 //Roo.log(nodeName);
20448 switch(nodeName.toLowerCase()) {
20450 switch(className) {
20456 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20457 switch(this.viewMode){
20459 this.viewDate = this.moveMonth(this.viewDate, dir);
20463 this.viewDate = this.moveYear(this.viewDate, dir);
20469 var date = new Date();
20470 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20472 this.setValue(this.formatDate(this.date));
20479 if (className.indexOf('disabled') < 0) {
20480 this.viewDate.setUTCDate(1);
20481 if (className.indexOf('month') > -1) {
20482 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20484 var year = parseInt(html, 10) || 0;
20485 this.viewDate.setUTCFullYear(year);
20489 if(this.singleMode){
20490 this.setValue(this.formatDate(this.viewDate));
20501 //Roo.log(className);
20502 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20503 var day = parseInt(html, 10) || 1;
20504 var year = this.viewDate.getUTCFullYear(),
20505 month = this.viewDate.getUTCMonth();
20507 if (className.indexOf('old') > -1) {
20514 } else if (className.indexOf('new') > -1) {
20522 //Roo.log([year,month,day]);
20523 this.date = this.UTCDate(year, month, day,0,0,0,0);
20524 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20526 //Roo.log(this.formatDate(this.date));
20527 this.setValue(this.formatDate(this.date));
20534 setStartDate: function(startDate)
20536 this.startDate = startDate || -Infinity;
20537 if (this.startDate !== -Infinity) {
20538 this.startDate = this.parseDate(this.startDate);
20541 this.updateNavArrows();
20544 setEndDate: function(endDate)
20546 this.endDate = endDate || Infinity;
20547 if (this.endDate !== Infinity) {
20548 this.endDate = this.parseDate(this.endDate);
20551 this.updateNavArrows();
20554 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20556 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20557 if (typeof(this.daysOfWeekDisabled) !== 'object') {
20558 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20560 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20561 return parseInt(d, 10);
20564 this.updateNavArrows();
20567 updateNavArrows: function()
20569 if(this.singleMode){
20573 var d = new Date(this.viewDate),
20574 year = d.getUTCFullYear(),
20575 month = d.getUTCMonth();
20577 Roo.each(this.picker().select('.prev', true).elements, function(v){
20579 switch (this.viewMode) {
20582 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20588 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20595 Roo.each(this.picker().select('.next', true).elements, function(v){
20597 switch (this.viewMode) {
20600 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20606 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20614 moveMonth: function(date, dir)
20619 var new_date = new Date(date.valueOf()),
20620 day = new_date.getUTCDate(),
20621 month = new_date.getUTCMonth(),
20622 mag = Math.abs(dir),
20624 dir = dir > 0 ? 1 : -1;
20627 // If going back one month, make sure month is not current month
20628 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20630 return new_date.getUTCMonth() == month;
20632 // If going forward one month, make sure month is as expected
20633 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20635 return new_date.getUTCMonth() != new_month;
20637 new_month = month + dir;
20638 new_date.setUTCMonth(new_month);
20639 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20640 if (new_month < 0 || new_month > 11) {
20641 new_month = (new_month + 12) % 12;
20644 // For magnitudes >1, move one month at a time...
20645 for (var i=0; i<mag; i++) {
20646 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20647 new_date = this.moveMonth(new_date, dir);
20649 // ...then reset the day, keeping it in the new month
20650 new_month = new_date.getUTCMonth();
20651 new_date.setUTCDate(day);
20653 return new_month != new_date.getUTCMonth();
20656 // Common date-resetting loop -- if date is beyond end of month, make it
20659 new_date.setUTCDate(--day);
20660 new_date.setUTCMonth(new_month);
20665 moveYear: function(date, dir)
20667 return this.moveMonth(date, dir*12);
20670 dateWithinRange: function(date)
20672 return date >= this.startDate && date <= this.endDate;
20678 this.picker().remove();
20681 validateValue : function(value)
20683 if(this.getVisibilityEl().hasClass('hidden')){
20687 if(value.length < 1) {
20688 if(this.allowBlank){
20694 if(value.length < this.minLength){
20697 if(value.length > this.maxLength){
20701 var vt = Roo.form.VTypes;
20702 if(!vt[this.vtype](value, this)){
20706 if(typeof this.validator == "function"){
20707 var msg = this.validator(value);
20713 if(this.regex && !this.regex.test(value)){
20717 if(typeof(this.parseDate(value)) == 'undefined'){
20721 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20725 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20735 this.date = this.viewDate = '';
20737 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20742 Roo.apply(Roo.bootstrap.DateField, {
20753 html: '<i class="fa fa-arrow-left"/>'
20763 html: '<i class="fa fa-arrow-right"/>'
20805 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20806 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20807 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20808 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20809 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20822 navFnc: 'FullYear',
20827 navFnc: 'FullYear',
20832 Roo.apply(Roo.bootstrap.DateField, {
20836 cls: 'datepicker dropdown-menu roo-dynamic',
20840 cls: 'datepicker-days',
20844 cls: 'table-condensed',
20846 Roo.bootstrap.DateField.head,
20850 Roo.bootstrap.DateField.footer
20857 cls: 'datepicker-months',
20861 cls: 'table-condensed',
20863 Roo.bootstrap.DateField.head,
20864 Roo.bootstrap.DateField.content,
20865 Roo.bootstrap.DateField.footer
20872 cls: 'datepicker-years',
20876 cls: 'table-condensed',
20878 Roo.bootstrap.DateField.head,
20879 Roo.bootstrap.DateField.content,
20880 Roo.bootstrap.DateField.footer
20899 * @class Roo.bootstrap.TimeField
20900 * @extends Roo.bootstrap.Input
20901 * Bootstrap DateField class
20905 * Create a new TimeField
20906 * @param {Object} config The config object
20909 Roo.bootstrap.TimeField = function(config){
20910 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20914 * Fires when this field show.
20915 * @param {Roo.bootstrap.DateField} thisthis
20916 * @param {Mixed} date The date value
20921 * Fires when this field hide.
20922 * @param {Roo.bootstrap.DateField} this
20923 * @param {Mixed} date The date value
20928 * Fires when select a date.
20929 * @param {Roo.bootstrap.DateField} this
20930 * @param {Mixed} date The date value
20936 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20939 * @cfg {String} format
20940 * The default time format string which can be overriden for localization support. The format must be
20941 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20945 onRender: function(ct, position)
20948 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20950 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20952 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20954 this.pop = this.picker().select('>.datepicker-time',true).first();
20955 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20957 this.picker().on('mousedown', this.onMousedown, this);
20958 this.picker().on('click', this.onClick, this);
20960 this.picker().addClass('datepicker-dropdown');
20965 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20966 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20967 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20968 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20969 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20970 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20974 fireKey: function(e){
20975 if (!this.picker().isVisible()){
20976 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20982 e.preventDefault();
20990 this.onTogglePeriod();
20993 this.onIncrementMinutes();
20996 this.onDecrementMinutes();
21005 onClick: function(e) {
21006 e.stopPropagation();
21007 e.preventDefault();
21010 picker : function()
21012 return this.el.select('.datepicker', true).first();
21015 fillTime: function()
21017 var time = this.pop.select('tbody', true).first();
21019 time.dom.innerHTML = '';
21034 cls: 'hours-up glyphicon glyphicon-chevron-up'
21054 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21075 cls: 'timepicker-hour',
21090 cls: 'timepicker-minute',
21105 cls: 'btn btn-primary period',
21127 cls: 'hours-down glyphicon glyphicon-chevron-down'
21147 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21165 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21172 var hours = this.time.getHours();
21173 var minutes = this.time.getMinutes();
21186 hours = hours - 12;
21190 hours = '0' + hours;
21194 minutes = '0' + minutes;
21197 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21198 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21199 this.pop.select('button', true).first().dom.innerHTML = period;
21205 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21207 var cls = ['bottom'];
21209 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21216 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21221 this.picker().addClass(cls.join('-'));
21225 Roo.each(cls, function(c){
21227 _this.picker().setTop(_this.inputEl().getHeight());
21231 _this.picker().setTop(0 - _this.picker().getHeight());
21236 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21240 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21247 onFocus : function()
21249 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21253 onBlur : function()
21255 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21261 this.picker().show();
21266 this.fireEvent('show', this, this.date);
21271 this.picker().hide();
21274 this.fireEvent('hide', this, this.date);
21277 setTime : function()
21280 this.setValue(this.time.format(this.format));
21282 this.fireEvent('select', this, this.date);
21287 onMousedown: function(e){
21288 e.stopPropagation();
21289 e.preventDefault();
21292 onIncrementHours: function()
21294 Roo.log('onIncrementHours');
21295 this.time = this.time.add(Date.HOUR, 1);
21300 onDecrementHours: function()
21302 Roo.log('onDecrementHours');
21303 this.time = this.time.add(Date.HOUR, -1);
21307 onIncrementMinutes: function()
21309 Roo.log('onIncrementMinutes');
21310 this.time = this.time.add(Date.MINUTE, 1);
21314 onDecrementMinutes: function()
21316 Roo.log('onDecrementMinutes');
21317 this.time = this.time.add(Date.MINUTE, -1);
21321 onTogglePeriod: function()
21323 Roo.log('onTogglePeriod');
21324 this.time = this.time.add(Date.HOUR, 12);
21331 Roo.apply(Roo.bootstrap.TimeField, {
21361 cls: 'btn btn-info ok',
21373 Roo.apply(Roo.bootstrap.TimeField, {
21377 cls: 'datepicker dropdown-menu',
21381 cls: 'datepicker-time',
21385 cls: 'table-condensed',
21387 Roo.bootstrap.TimeField.content,
21388 Roo.bootstrap.TimeField.footer
21407 * @class Roo.bootstrap.MonthField
21408 * @extends Roo.bootstrap.Input
21409 * Bootstrap MonthField class
21411 * @cfg {String} language default en
21414 * Create a new MonthField
21415 * @param {Object} config The config object
21418 Roo.bootstrap.MonthField = function(config){
21419 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21424 * Fires when this field show.
21425 * @param {Roo.bootstrap.MonthField} this
21426 * @param {Mixed} date The date value
21431 * Fires when this field hide.
21432 * @param {Roo.bootstrap.MonthField} this
21433 * @param {Mixed} date The date value
21438 * Fires when select a date.
21439 * @param {Roo.bootstrap.MonthField} this
21440 * @param {String} oldvalue The old value
21441 * @param {String} newvalue The new value
21447 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
21449 onRender: function(ct, position)
21452 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21454 this.language = this.language || 'en';
21455 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21456 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21458 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21459 this.isInline = false;
21460 this.isInput = true;
21461 this.component = this.el.select('.add-on', true).first() || false;
21462 this.component = (this.component && this.component.length === 0) ? false : this.component;
21463 this.hasInput = this.component && this.inputEL().length;
21465 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21467 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21469 this.picker().on('mousedown', this.onMousedown, this);
21470 this.picker().on('click', this.onClick, this);
21472 this.picker().addClass('datepicker-dropdown');
21474 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21475 v.setStyle('width', '189px');
21482 if(this.isInline) {
21488 setValue: function(v, suppressEvent)
21490 var o = this.getValue();
21492 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21496 if(suppressEvent !== true){
21497 this.fireEvent('select', this, o, v);
21502 getValue: function()
21507 onClick: function(e)
21509 e.stopPropagation();
21510 e.preventDefault();
21512 var target = e.getTarget();
21514 if(target.nodeName.toLowerCase() === 'i'){
21515 target = Roo.get(target).dom.parentNode;
21518 var nodeName = target.nodeName;
21519 var className = target.className;
21520 var html = target.innerHTML;
21522 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21526 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21528 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21534 picker : function()
21536 return this.pickerEl;
21539 fillMonths: function()
21542 var months = this.picker().select('>.datepicker-months td', true).first();
21544 months.dom.innerHTML = '';
21550 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21553 months.createChild(month);
21562 if(typeof(this.vIndex) == 'undefined' && this.value.length){
21563 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21566 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21567 e.removeClass('active');
21569 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21570 e.addClass('active');
21577 if(this.isInline) {
21581 this.picker().removeClass(['bottom', 'top']);
21583 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21585 * place to the top of element!
21589 this.picker().addClass('top');
21590 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21595 this.picker().addClass('bottom');
21597 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21600 onFocus : function()
21602 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21606 onBlur : function()
21608 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21610 var d = this.inputEl().getValue();
21619 this.picker().show();
21620 this.picker().select('>.datepicker-months', true).first().show();
21624 this.fireEvent('show', this, this.date);
21629 if(this.isInline) {
21632 this.picker().hide();
21633 this.fireEvent('hide', this, this.date);
21637 onMousedown: function(e)
21639 e.stopPropagation();
21640 e.preventDefault();
21645 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21649 fireKey: function(e)
21651 if (!this.picker().isVisible()){
21652 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21663 e.preventDefault();
21667 dir = e.keyCode == 37 ? -1 : 1;
21669 this.vIndex = this.vIndex + dir;
21671 if(this.vIndex < 0){
21675 if(this.vIndex > 11){
21679 if(isNaN(this.vIndex)){
21683 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21689 dir = e.keyCode == 38 ? -1 : 1;
21691 this.vIndex = this.vIndex + dir * 4;
21693 if(this.vIndex < 0){
21697 if(this.vIndex > 11){
21701 if(isNaN(this.vIndex)){
21705 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21710 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21711 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21715 e.preventDefault();
21718 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21719 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21735 this.picker().remove();
21740 Roo.apply(Roo.bootstrap.MonthField, {
21759 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21760 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21765 Roo.apply(Roo.bootstrap.MonthField, {
21769 cls: 'datepicker dropdown-menu roo-dynamic',
21773 cls: 'datepicker-months',
21777 cls: 'table-condensed',
21779 Roo.bootstrap.DateField.content
21799 * @class Roo.bootstrap.CheckBox
21800 * @extends Roo.bootstrap.Input
21801 * Bootstrap CheckBox class
21803 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21804 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21805 * @cfg {String} boxLabel The text that appears beside the checkbox
21806 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21807 * @cfg {Boolean} checked initnal the element
21808 * @cfg {Boolean} inline inline the element (default false)
21809 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21810 * @cfg {String} tooltip label tooltip
21813 * Create a new CheckBox
21814 * @param {Object} config The config object
21817 Roo.bootstrap.CheckBox = function(config){
21818 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21823 * Fires when the element is checked or unchecked.
21824 * @param {Roo.bootstrap.CheckBox} this This input
21825 * @param {Boolean} checked The new checked value
21830 * Fires when the element is click.
21831 * @param {Roo.bootstrap.CheckBox} this This input
21838 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21840 inputType: 'checkbox',
21849 // checkbox success does not make any sense really..
21854 getAutoCreate : function()
21856 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21862 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21865 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21871 type : this.inputType,
21872 value : this.inputValue,
21873 cls : 'roo-' + this.inputType, //'form-box',
21874 placeholder : this.placeholder || ''
21878 if(this.inputType != 'radio'){
21882 cls : 'roo-hidden-value',
21883 value : this.checked ? this.inputValue : this.valueOff
21888 if (this.weight) { // Validity check?
21889 cfg.cls += " " + this.inputType + "-" + this.weight;
21892 if (this.disabled) {
21893 input.disabled=true;
21897 input.checked = this.checked;
21902 input.name = this.name;
21904 if(this.inputType != 'radio'){
21905 hidden.name = this.name;
21906 input.name = '_hidden_' + this.name;
21911 input.cls += ' input-' + this.size;
21916 ['xs','sm','md','lg'].map(function(size){
21917 if (settings[size]) {
21918 cfg.cls += ' col-' + size + '-' + settings[size];
21922 var inputblock = input;
21924 if (this.before || this.after) {
21927 cls : 'input-group',
21932 inputblock.cn.push({
21934 cls : 'input-group-addon',
21939 inputblock.cn.push(input);
21941 if(this.inputType != 'radio'){
21942 inputblock.cn.push(hidden);
21946 inputblock.cn.push({
21948 cls : 'input-group-addon',
21954 var boxLabelCfg = false;
21960 //'for': id, // box label is handled by onclick - so no for...
21962 html: this.boxLabel
21965 boxLabelCfg.tooltip = this.tooltip;
21971 if (align ==='left' && this.fieldLabel.length) {
21972 // Roo.log("left and has label");
21977 cls : 'control-label',
21978 html : this.fieldLabel
21989 cfg.cn[1].cn.push(boxLabelCfg);
21992 if(this.labelWidth > 12){
21993 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21996 if(this.labelWidth < 13 && this.labelmd == 0){
21997 this.labelmd = this.labelWidth;
22000 if(this.labellg > 0){
22001 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22002 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22005 if(this.labelmd > 0){
22006 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22007 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22010 if(this.labelsm > 0){
22011 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22012 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22015 if(this.labelxs > 0){
22016 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22017 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22020 } else if ( this.fieldLabel.length) {
22021 // Roo.log(" label");
22025 tag: this.boxLabel ? 'span' : 'label',
22027 cls: 'control-label box-input-label',
22028 //cls : 'input-group-addon',
22029 html : this.fieldLabel
22036 cfg.cn.push(boxLabelCfg);
22041 // Roo.log(" no label && no align");
22042 cfg.cn = [ inputblock ] ;
22044 cfg.cn.push(boxLabelCfg);
22052 if(this.inputType != 'radio'){
22053 cfg.cn.push(hidden);
22061 * return the real input element.
22063 inputEl: function ()
22065 return this.el.select('input.roo-' + this.inputType,true).first();
22067 hiddenEl: function ()
22069 return this.el.select('input.roo-hidden-value',true).first();
22072 labelEl: function()
22074 return this.el.select('label.control-label',true).first();
22076 /* depricated... */
22080 return this.labelEl();
22083 boxLabelEl: function()
22085 return this.el.select('label.box-label',true).first();
22088 initEvents : function()
22090 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22092 this.inputEl().on('click', this.onClick, this);
22094 if (this.boxLabel) {
22095 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22098 this.startValue = this.getValue();
22101 Roo.bootstrap.CheckBox.register(this);
22105 onClick : function(e)
22107 if(this.fireEvent('click', this, e) !== false){
22108 this.setChecked(!this.checked);
22113 setChecked : function(state,suppressEvent)
22115 this.startValue = this.getValue();
22117 if(this.inputType == 'radio'){
22119 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22120 e.dom.checked = false;
22123 this.inputEl().dom.checked = true;
22125 this.inputEl().dom.value = this.inputValue;
22127 if(suppressEvent !== true){
22128 this.fireEvent('check', this, true);
22136 this.checked = state;
22138 this.inputEl().dom.checked = state;
22141 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22143 if(suppressEvent !== true){
22144 this.fireEvent('check', this, state);
22150 getValue : function()
22152 if(this.inputType == 'radio'){
22153 return this.getGroupValue();
22156 return this.hiddenEl().dom.value;
22160 getGroupValue : function()
22162 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22166 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22169 setValue : function(v,suppressEvent)
22171 if(this.inputType == 'radio'){
22172 this.setGroupValue(v, suppressEvent);
22176 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22181 setGroupValue : function(v, suppressEvent)
22183 this.startValue = this.getValue();
22185 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22186 e.dom.checked = false;
22188 if(e.dom.value == v){
22189 e.dom.checked = true;
22193 if(suppressEvent !== true){
22194 this.fireEvent('check', this, true);
22202 validate : function()
22204 if(this.getVisibilityEl().hasClass('hidden')){
22210 (this.inputType == 'radio' && this.validateRadio()) ||
22211 (this.inputType == 'checkbox' && this.validateCheckbox())
22217 this.markInvalid();
22221 validateRadio : function()
22223 if(this.getVisibilityEl().hasClass('hidden')){
22227 if(this.allowBlank){
22233 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22234 if(!e.dom.checked){
22246 validateCheckbox : function()
22249 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22250 //return (this.getValue() == this.inputValue) ? true : false;
22253 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22261 for(var i in group){
22262 if(group[i].el.isVisible(true)){
22270 for(var i in group){
22275 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22282 * Mark this field as valid
22284 markValid : function()
22288 this.fireEvent('valid', this);
22290 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22293 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22300 if(this.inputType == 'radio'){
22301 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22302 var fg = e.findParent('.form-group', false, true);
22303 if (Roo.bootstrap.version == 3) {
22304 fg.removeClass([_this.invalidClass, _this.validClass]);
22305 fg.addClass(_this.validClass);
22307 fg.removeClass(['is-valid', 'is-invalid']);
22308 fg.addClass('is-valid');
22316 var fg = this.el.findParent('.form-group', false, true);
22317 if (Roo.bootstrap.version == 3) {
22318 fg.removeClass([this.invalidClass, this.validClass]);
22319 fg.addClass(this.validClass);
22321 fg.removeClass(['is-valid', 'is-invalid']);
22322 fg.addClass('is-valid');
22327 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22333 for(var i in group){
22334 var fg = group[i].el.findParent('.form-group', false, true);
22335 if (Roo.bootstrap.version == 3) {
22336 fg.removeClass([this.invalidClass, this.validClass]);
22337 fg.addClass(this.validClass);
22339 fg.removeClass(['is-valid', 'is-invalid']);
22340 fg.addClass('is-valid');
22346 * Mark this field as invalid
22347 * @param {String} msg The validation message
22349 markInvalid : function(msg)
22351 if(this.allowBlank){
22357 this.fireEvent('invalid', this, msg);
22359 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22362 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22366 label.markInvalid();
22369 if(this.inputType == 'radio'){
22371 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22372 var fg = e.findParent('.form-group', false, true);
22373 if (Roo.bootstrap.version == 3) {
22374 fg.removeClass([_this.invalidClass, _this.validClass]);
22375 fg.addClass(_this.invalidClass);
22377 fg.removeClass(['is-invalid', 'is-valid']);
22378 fg.addClass('is-invalid');
22386 var fg = this.el.findParent('.form-group', false, true);
22387 if (Roo.bootstrap.version == 3) {
22388 fg.removeClass([_this.invalidClass, _this.validClass]);
22389 fg.addClass(_this.invalidClass);
22391 fg.removeClass(['is-invalid', 'is-valid']);
22392 fg.addClass('is-invalid');
22397 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22403 for(var i in group){
22404 var fg = group[i].el.findParent('.form-group', false, true);
22405 if (Roo.bootstrap.version == 3) {
22406 fg.removeClass([_this.invalidClass, _this.validClass]);
22407 fg.addClass(_this.invalidClass);
22409 fg.removeClass(['is-invalid', 'is-valid']);
22410 fg.addClass('is-invalid');
22416 clearInvalid : function()
22418 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22420 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22422 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22424 if (label && label.iconEl) {
22425 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22426 label.iconEl.removeClass(['is-invalid', 'is-valid']);
22430 disable : function()
22432 if(this.inputType != 'radio'){
22433 Roo.bootstrap.CheckBox.superclass.disable.call(this);
22440 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22441 _this.getActionEl().addClass(this.disabledClass);
22442 e.dom.disabled = true;
22446 this.disabled = true;
22447 this.fireEvent("disable", this);
22451 enable : function()
22453 if(this.inputType != 'radio'){
22454 Roo.bootstrap.CheckBox.superclass.enable.call(this);
22461 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22462 _this.getActionEl().removeClass(this.disabledClass);
22463 e.dom.disabled = false;
22467 this.disabled = false;
22468 this.fireEvent("enable", this);
22472 setBoxLabel : function(v)
22477 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22483 Roo.apply(Roo.bootstrap.CheckBox, {
22488 * register a CheckBox Group
22489 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22491 register : function(checkbox)
22493 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22494 this.groups[checkbox.groupId] = {};
22497 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22501 this.groups[checkbox.groupId][checkbox.name] = checkbox;
22505 * fetch a CheckBox Group based on the group ID
22506 * @param {string} the group ID
22507 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22509 get: function(groupId) {
22510 if (typeof(this.groups[groupId]) == 'undefined') {
22514 return this.groups[groupId] ;
22527 * @class Roo.bootstrap.Radio
22528 * @extends Roo.bootstrap.Component
22529 * Bootstrap Radio class
22530 * @cfg {String} boxLabel - the label associated
22531 * @cfg {String} value - the value of radio
22534 * Create a new Radio
22535 * @param {Object} config The config object
22537 Roo.bootstrap.Radio = function(config){
22538 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22542 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22548 getAutoCreate : function()
22552 cls : 'form-group radio',
22557 html : this.boxLabel
22565 initEvents : function()
22567 this.parent().register(this);
22569 this.el.on('click', this.onClick, this);
22573 onClick : function(e)
22575 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22576 this.setChecked(true);
22580 setChecked : function(state, suppressEvent)
22582 this.parent().setValue(this.value, suppressEvent);
22586 setBoxLabel : function(v)
22591 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22606 * @class Roo.bootstrap.SecurePass
22607 * @extends Roo.bootstrap.Input
22608 * Bootstrap SecurePass class
22612 * Create a new SecurePass
22613 * @param {Object} config The config object
22616 Roo.bootstrap.SecurePass = function (config) {
22617 // these go here, so the translation tool can replace them..
22619 PwdEmpty: "Please type a password, and then retype it to confirm.",
22620 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22621 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22622 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22623 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22624 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22625 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22626 TooWeak: "Your password is Too Weak."
22628 this.meterLabel = "Password strength:";
22629 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22630 this.meterClass = [
22631 "roo-password-meter-tooweak",
22632 "roo-password-meter-weak",
22633 "roo-password-meter-medium",
22634 "roo-password-meter-strong",
22635 "roo-password-meter-grey"
22640 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22643 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22645 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22647 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22648 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22649 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22650 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22651 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22652 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22653 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22663 * @cfg {String/Object} Label for the strength meter (defaults to
22664 * 'Password strength:')
22669 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22670 * ['Weak', 'Medium', 'Strong'])
22673 pwdStrengths: false,
22686 initEvents: function ()
22688 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22690 if (this.el.is('input[type=password]') && Roo.isSafari) {
22691 this.el.on('keydown', this.SafariOnKeyDown, this);
22694 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22697 onRender: function (ct, position)
22699 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22700 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22701 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22703 this.trigger.createChild({
22708 cls: 'roo-password-meter-grey col-xs-12',
22711 //width: this.meterWidth + 'px'
22715 cls: 'roo-password-meter-text'
22721 if (this.hideTrigger) {
22722 this.trigger.setDisplayed(false);
22724 this.setSize(this.width || '', this.height || '');
22727 onDestroy: function ()
22729 if (this.trigger) {
22730 this.trigger.removeAllListeners();
22731 this.trigger.remove();
22734 this.wrap.remove();
22736 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22739 checkStrength: function ()
22741 var pwd = this.inputEl().getValue();
22742 if (pwd == this._lastPwd) {
22747 if (this.ClientSideStrongPassword(pwd)) {
22749 } else if (this.ClientSideMediumPassword(pwd)) {
22751 } else if (this.ClientSideWeakPassword(pwd)) {
22757 Roo.log('strength1: ' + strength);
22759 //var pm = this.trigger.child('div/div/div').dom;
22760 var pm = this.trigger.child('div/div');
22761 pm.removeClass(this.meterClass);
22762 pm.addClass(this.meterClass[strength]);
22765 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22767 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22769 this._lastPwd = pwd;
22773 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22775 this._lastPwd = '';
22777 var pm = this.trigger.child('div/div');
22778 pm.removeClass(this.meterClass);
22779 pm.addClass('roo-password-meter-grey');
22782 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22785 this.inputEl().dom.type='password';
22788 validateValue: function (value)
22791 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22794 if (value.length == 0) {
22795 if (this.allowBlank) {
22796 this.clearInvalid();
22800 this.markInvalid(this.errors.PwdEmpty);
22801 this.errorMsg = this.errors.PwdEmpty;
22809 if ('[\x21-\x7e]*'.match(value)) {
22810 this.markInvalid(this.errors.PwdBadChar);
22811 this.errorMsg = this.errors.PwdBadChar;
22814 if (value.length < 6) {
22815 this.markInvalid(this.errors.PwdShort);
22816 this.errorMsg = this.errors.PwdShort;
22819 if (value.length > 16) {
22820 this.markInvalid(this.errors.PwdLong);
22821 this.errorMsg = this.errors.PwdLong;
22825 if (this.ClientSideStrongPassword(value)) {
22827 } else if (this.ClientSideMediumPassword(value)) {
22829 } else if (this.ClientSideWeakPassword(value)) {
22836 if (strength < 2) {
22837 //this.markInvalid(this.errors.TooWeak);
22838 this.errorMsg = this.errors.TooWeak;
22843 console.log('strength2: ' + strength);
22845 //var pm = this.trigger.child('div/div/div').dom;
22847 var pm = this.trigger.child('div/div');
22848 pm.removeClass(this.meterClass);
22849 pm.addClass(this.meterClass[strength]);
22851 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22853 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22855 this.errorMsg = '';
22859 CharacterSetChecks: function (type)
22862 this.fResult = false;
22865 isctype: function (character, type)
22868 case this.kCapitalLetter:
22869 if (character >= 'A' && character <= 'Z') {
22874 case this.kSmallLetter:
22875 if (character >= 'a' && character <= 'z') {
22881 if (character >= '0' && character <= '9') {
22886 case this.kPunctuation:
22887 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22898 IsLongEnough: function (pwd, size)
22900 return !(pwd == null || isNaN(size) || pwd.length < size);
22903 SpansEnoughCharacterSets: function (word, nb)
22905 if (!this.IsLongEnough(word, nb))
22910 var characterSetChecks = new Array(
22911 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22912 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22915 for (var index = 0; index < word.length; ++index) {
22916 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22917 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22918 characterSetChecks[nCharSet].fResult = true;
22925 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22926 if (characterSetChecks[nCharSet].fResult) {
22931 if (nCharSets < nb) {
22937 ClientSideStrongPassword: function (pwd)
22939 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22942 ClientSideMediumPassword: function (pwd)
22944 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22947 ClientSideWeakPassword: function (pwd)
22949 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22952 })//<script type="text/javascript">
22955 * Based Ext JS Library 1.1.1
22956 * Copyright(c) 2006-2007, Ext JS, LLC.
22962 * @class Roo.HtmlEditorCore
22963 * @extends Roo.Component
22964 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22966 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22969 Roo.HtmlEditorCore = function(config){
22972 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22977 * @event initialize
22978 * Fires when the editor is fully initialized (including the iframe)
22979 * @param {Roo.HtmlEditorCore} this
22984 * Fires when the editor is first receives the focus. Any insertion must wait
22985 * until after this event.
22986 * @param {Roo.HtmlEditorCore} this
22990 * @event beforesync
22991 * Fires before the textarea is updated with content from the editor iframe. Return false
22992 * to cancel the sync.
22993 * @param {Roo.HtmlEditorCore} this
22994 * @param {String} html
22998 * @event beforepush
22999 * Fires before the iframe editor is updated with content from the textarea. Return false
23000 * to cancel the push.
23001 * @param {Roo.HtmlEditorCore} this
23002 * @param {String} html
23007 * Fires when the textarea is updated with content from the editor iframe.
23008 * @param {Roo.HtmlEditorCore} this
23009 * @param {String} html
23014 * Fires when the iframe editor is updated with content from the textarea.
23015 * @param {Roo.HtmlEditorCore} this
23016 * @param {String} html
23021 * @event editorevent
23022 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23023 * @param {Roo.HtmlEditorCore} this
23029 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23031 // defaults : white / black...
23032 this.applyBlacklists();
23039 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23043 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23049 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23054 * @cfg {Number} height (in pixels)
23058 * @cfg {Number} width (in pixels)
23063 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23066 stylesheets: false,
23071 // private properties
23072 validationEvent : false,
23074 initialized : false,
23076 sourceEditMode : false,
23077 onFocus : Roo.emptyFn,
23079 hideMode:'offsets',
23083 // blacklist + whitelisted elements..
23090 * Protected method that will not generally be called directly. It
23091 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23092 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23094 getDocMarkup : function(){
23098 // inherit styels from page...??
23099 if (this.stylesheets === false) {
23101 Roo.get(document.head).select('style').each(function(node) {
23102 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23105 Roo.get(document.head).select('link').each(function(node) {
23106 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23109 } else if (!this.stylesheets.length) {
23111 st = '<style type="text/css">' +
23112 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23115 st = '<style type="text/css">' +
23120 st += '<style type="text/css">' +
23121 'IMG { cursor: pointer } ' +
23124 var cls = 'roo-htmleditor-body';
23126 if(this.bodyCls.length){
23127 cls += ' ' + this.bodyCls;
23130 return '<html><head>' + st +
23131 //<style type="text/css">' +
23132 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23134 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23138 onRender : function(ct, position)
23141 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23142 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23145 this.el.dom.style.border = '0 none';
23146 this.el.dom.setAttribute('tabIndex', -1);
23147 this.el.addClass('x-hidden hide');
23151 if(Roo.isIE){ // fix IE 1px bogus margin
23152 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23156 this.frameId = Roo.id();
23160 var iframe = this.owner.wrap.createChild({
23162 cls: 'form-control', // bootstrap..
23164 name: this.frameId,
23165 frameBorder : 'no',
23166 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23171 this.iframe = iframe.dom;
23173 this.assignDocWin();
23175 this.doc.designMode = 'on';
23178 this.doc.write(this.getDocMarkup());
23182 var task = { // must defer to wait for browser to be ready
23184 //console.log("run task?" + this.doc.readyState);
23185 this.assignDocWin();
23186 if(this.doc.body || this.doc.readyState == 'complete'){
23188 this.doc.designMode="on";
23192 Roo.TaskMgr.stop(task);
23193 this.initEditor.defer(10, this);
23200 Roo.TaskMgr.start(task);
23205 onResize : function(w, h)
23207 Roo.log('resize: ' +w + ',' + h );
23208 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23212 if(typeof w == 'number'){
23214 this.iframe.style.width = w + 'px';
23216 if(typeof h == 'number'){
23218 this.iframe.style.height = h + 'px';
23220 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23227 * Toggles the editor between standard and source edit mode.
23228 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23230 toggleSourceEdit : function(sourceEditMode){
23232 this.sourceEditMode = sourceEditMode === true;
23234 if(this.sourceEditMode){
23236 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23239 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23240 //this.iframe.className = '';
23243 //this.setSize(this.owner.wrap.getSize());
23244 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23251 * Protected method that will not generally be called directly. If you need/want
23252 * custom HTML cleanup, this is the method you should override.
23253 * @param {String} html The HTML to be cleaned
23254 * return {String} The cleaned HTML
23256 cleanHtml : function(html){
23257 html = String(html);
23258 if(html.length > 5){
23259 if(Roo.isSafari){ // strip safari nonsense
23260 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23263 if(html == ' '){
23270 * HTML Editor -> Textarea
23271 * Protected method that will not generally be called directly. Syncs the contents
23272 * of the editor iframe with the textarea.
23274 syncValue : function(){
23275 if(this.initialized){
23276 var bd = (this.doc.body || this.doc.documentElement);
23277 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23278 var html = bd.innerHTML;
23280 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23281 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23283 html = '<div style="'+m[0]+'">' + html + '</div>';
23286 html = this.cleanHtml(html);
23287 // fix up the special chars.. normaly like back quotes in word...
23288 // however we do not want to do this with chinese..
23289 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23291 var cc = match.charCodeAt();
23293 // Get the character value, handling surrogate pairs
23294 if (match.length == 2) {
23295 // It's a surrogate pair, calculate the Unicode code point
23296 var high = match.charCodeAt(0) - 0xD800;
23297 var low = match.charCodeAt(1) - 0xDC00;
23298 cc = (high * 0x400) + low + 0x10000;
23300 (cc >= 0x4E00 && cc < 0xA000 ) ||
23301 (cc >= 0x3400 && cc < 0x4E00 ) ||
23302 (cc >= 0xf900 && cc < 0xfb00 )
23307 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23308 return "&#" + cc + ";";
23315 if(this.owner.fireEvent('beforesync', this, html) !== false){
23316 this.el.dom.value = html;
23317 this.owner.fireEvent('sync', this, html);
23323 * Protected method that will not generally be called directly. Pushes the value of the textarea
23324 * into the iframe editor.
23326 pushValue : function(){
23327 if(this.initialized){
23328 var v = this.el.dom.value.trim();
23330 // if(v.length < 1){
23334 if(this.owner.fireEvent('beforepush', this, v) !== false){
23335 var d = (this.doc.body || this.doc.documentElement);
23337 this.cleanUpPaste();
23338 this.el.dom.value = d.innerHTML;
23339 this.owner.fireEvent('push', this, v);
23345 deferFocus : function(){
23346 this.focus.defer(10, this);
23350 focus : function(){
23351 if(this.win && !this.sourceEditMode){
23358 assignDocWin: function()
23360 var iframe = this.iframe;
23363 this.doc = iframe.contentWindow.document;
23364 this.win = iframe.contentWindow;
23366 // if (!Roo.get(this.frameId)) {
23369 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23370 // this.win = Roo.get(this.frameId).dom.contentWindow;
23372 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23376 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23377 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23382 initEditor : function(){
23383 //console.log("INIT EDITOR");
23384 this.assignDocWin();
23388 this.doc.designMode="on";
23390 this.doc.write(this.getDocMarkup());
23393 var dbody = (this.doc.body || this.doc.documentElement);
23394 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23395 // this copies styles from the containing element into thsi one..
23396 // not sure why we need all of this..
23397 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23399 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23400 //ss['background-attachment'] = 'fixed'; // w3c
23401 dbody.bgProperties = 'fixed'; // ie
23402 //Roo.DomHelper.applyStyles(dbody, ss);
23403 Roo.EventManager.on(this.doc, {
23404 //'mousedown': this.onEditorEvent,
23405 'mouseup': this.onEditorEvent,
23406 'dblclick': this.onEditorEvent,
23407 'click': this.onEditorEvent,
23408 'keyup': this.onEditorEvent,
23413 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23415 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23416 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23418 this.initialized = true;
23420 this.owner.fireEvent('initialize', this);
23425 onDestroy : function(){
23431 //for (var i =0; i < this.toolbars.length;i++) {
23432 // // fixme - ask toolbars for heights?
23433 // this.toolbars[i].onDestroy();
23436 //this.wrap.dom.innerHTML = '';
23437 //this.wrap.remove();
23442 onFirstFocus : function(){
23444 this.assignDocWin();
23447 this.activated = true;
23450 if(Roo.isGecko){ // prevent silly gecko errors
23452 var s = this.win.getSelection();
23453 if(!s.focusNode || s.focusNode.nodeType != 3){
23454 var r = s.getRangeAt(0);
23455 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23460 this.execCmd('useCSS', true);
23461 this.execCmd('styleWithCSS', false);
23464 this.owner.fireEvent('activate', this);
23468 adjustFont: function(btn){
23469 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23470 //if(Roo.isSafari){ // safari
23473 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23474 if(Roo.isSafari){ // safari
23475 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23476 v = (v < 10) ? 10 : v;
23477 v = (v > 48) ? 48 : v;
23478 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23483 v = Math.max(1, v+adjust);
23485 this.execCmd('FontSize', v );
23488 onEditorEvent : function(e)
23490 this.owner.fireEvent('editorevent', this, e);
23491 // this.updateToolbar();
23492 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23495 insertTag : function(tg)
23497 // could be a bit smarter... -> wrap the current selected tRoo..
23498 if (tg.toLowerCase() == 'span' ||
23499 tg.toLowerCase() == 'code' ||
23500 tg.toLowerCase() == 'sup' ||
23501 tg.toLowerCase() == 'sub'
23504 range = this.createRange(this.getSelection());
23505 var wrappingNode = this.doc.createElement(tg.toLowerCase());
23506 wrappingNode.appendChild(range.extractContents());
23507 range.insertNode(wrappingNode);
23514 this.execCmd("formatblock", tg);
23518 insertText : function(txt)
23522 var range = this.createRange();
23523 range.deleteContents();
23524 //alert(Sender.getAttribute('label'));
23526 range.insertNode(this.doc.createTextNode(txt));
23532 * Executes a Midas editor command on the editor document and performs necessary focus and
23533 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23534 * @param {String} cmd The Midas command
23535 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23537 relayCmd : function(cmd, value){
23539 this.execCmd(cmd, value);
23540 this.owner.fireEvent('editorevent', this);
23541 //this.updateToolbar();
23542 this.owner.deferFocus();
23546 * Executes a Midas editor command directly on the editor document.
23547 * For visual commands, you should use {@link #relayCmd} instead.
23548 * <b>This should only be called after the editor is initialized.</b>
23549 * @param {String} cmd The Midas command
23550 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23552 execCmd : function(cmd, value){
23553 this.doc.execCommand(cmd, false, value === undefined ? null : value);
23560 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23562 * @param {String} text | dom node..
23564 insertAtCursor : function(text)
23567 if(!this.activated){
23573 var r = this.doc.selection.createRange();
23584 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23588 // from jquery ui (MIT licenced)
23590 var win = this.win;
23592 if (win.getSelection && win.getSelection().getRangeAt) {
23593 range = win.getSelection().getRangeAt(0);
23594 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23595 range.insertNode(node);
23596 } else if (win.document.selection && win.document.selection.createRange) {
23597 // no firefox support
23598 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23599 win.document.selection.createRange().pasteHTML(txt);
23601 // no firefox support
23602 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23603 this.execCmd('InsertHTML', txt);
23612 mozKeyPress : function(e){
23614 var c = e.getCharCode(), cmd;
23617 c = String.fromCharCode(c).toLowerCase();
23631 this.cleanUpPaste.defer(100, this);
23639 e.preventDefault();
23647 fixKeys : function(){ // load time branching for fastest keydown performance
23649 return function(e){
23650 var k = e.getKey(), r;
23653 r = this.doc.selection.createRange();
23656 r.pasteHTML('    ');
23663 r = this.doc.selection.createRange();
23665 var target = r.parentElement();
23666 if(!target || target.tagName.toLowerCase() != 'li'){
23668 r.pasteHTML('<br />');
23674 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23675 this.cleanUpPaste.defer(100, this);
23681 }else if(Roo.isOpera){
23682 return function(e){
23683 var k = e.getKey();
23687 this.execCmd('InsertHTML','    ');
23690 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23691 this.cleanUpPaste.defer(100, this);
23696 }else if(Roo.isSafari){
23697 return function(e){
23698 var k = e.getKey();
23702 this.execCmd('InsertText','\t');
23706 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23707 this.cleanUpPaste.defer(100, this);
23715 getAllAncestors: function()
23717 var p = this.getSelectedNode();
23720 a.push(p); // push blank onto stack..
23721 p = this.getParentElement();
23725 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23729 a.push(this.doc.body);
23733 lastSelNode : false,
23736 getSelection : function()
23738 this.assignDocWin();
23739 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23742 getSelectedNode: function()
23744 // this may only work on Gecko!!!
23746 // should we cache this!!!!
23751 var range = this.createRange(this.getSelection()).cloneRange();
23754 var parent = range.parentElement();
23756 var testRange = range.duplicate();
23757 testRange.moveToElementText(parent);
23758 if (testRange.inRange(range)) {
23761 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23764 parent = parent.parentElement;
23769 // is ancestor a text element.
23770 var ac = range.commonAncestorContainer;
23771 if (ac.nodeType == 3) {
23772 ac = ac.parentNode;
23775 var ar = ac.childNodes;
23778 var other_nodes = [];
23779 var has_other_nodes = false;
23780 for (var i=0;i<ar.length;i++) {
23781 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23784 // fullly contained node.
23786 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23791 // probably selected..
23792 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23793 other_nodes.push(ar[i]);
23797 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23802 has_other_nodes = true;
23804 if (!nodes.length && other_nodes.length) {
23805 nodes= other_nodes;
23807 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23813 createRange: function(sel)
23815 // this has strange effects when using with
23816 // top toolbar - not sure if it's a great idea.
23817 //this.editor.contentWindow.focus();
23818 if (typeof sel != "undefined") {
23820 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23822 return this.doc.createRange();
23825 return this.doc.createRange();
23828 getParentElement: function()
23831 this.assignDocWin();
23832 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23834 var range = this.createRange(sel);
23837 var p = range.commonAncestorContainer;
23838 while (p.nodeType == 3) { // text node
23849 * Range intersection.. the hard stuff...
23853 * [ -- selected range --- ]
23857 * if end is before start or hits it. fail.
23858 * if start is after end or hits it fail.
23860 * if either hits (but other is outside. - then it's not
23866 // @see http://www.thismuchiknow.co.uk/?p=64.
23867 rangeIntersectsNode : function(range, node)
23869 var nodeRange = node.ownerDocument.createRange();
23871 nodeRange.selectNode(node);
23873 nodeRange.selectNodeContents(node);
23876 var rangeStartRange = range.cloneRange();
23877 rangeStartRange.collapse(true);
23879 var rangeEndRange = range.cloneRange();
23880 rangeEndRange.collapse(false);
23882 var nodeStartRange = nodeRange.cloneRange();
23883 nodeStartRange.collapse(true);
23885 var nodeEndRange = nodeRange.cloneRange();
23886 nodeEndRange.collapse(false);
23888 return rangeStartRange.compareBoundaryPoints(
23889 Range.START_TO_START, nodeEndRange) == -1 &&
23890 rangeEndRange.compareBoundaryPoints(
23891 Range.START_TO_START, nodeStartRange) == 1;
23895 rangeCompareNode : function(range, node)
23897 var nodeRange = node.ownerDocument.createRange();
23899 nodeRange.selectNode(node);
23901 nodeRange.selectNodeContents(node);
23905 range.collapse(true);
23907 nodeRange.collapse(true);
23909 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23910 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23912 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23914 var nodeIsBefore = ss == 1;
23915 var nodeIsAfter = ee == -1;
23917 if (nodeIsBefore && nodeIsAfter) {
23920 if (!nodeIsBefore && nodeIsAfter) {
23921 return 1; //right trailed.
23924 if (nodeIsBefore && !nodeIsAfter) {
23925 return 2; // left trailed.
23931 // private? - in a new class?
23932 cleanUpPaste : function()
23934 // cleans up the whole document..
23935 Roo.log('cleanuppaste');
23937 this.cleanUpChildren(this.doc.body);
23938 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23939 if (clean != this.doc.body.innerHTML) {
23940 this.doc.body.innerHTML = clean;
23945 cleanWordChars : function(input) {// change the chars to hex code
23946 var he = Roo.HtmlEditorCore;
23948 var output = input;
23949 Roo.each(he.swapCodes, function(sw) {
23950 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23952 output = output.replace(swapper, sw[1]);
23959 cleanUpChildren : function (n)
23961 if (!n.childNodes.length) {
23964 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23965 this.cleanUpChild(n.childNodes[i]);
23972 cleanUpChild : function (node)
23975 //console.log(node);
23976 if (node.nodeName == "#text") {
23977 // clean up silly Windows -- stuff?
23980 if (node.nodeName == "#comment") {
23981 node.parentNode.removeChild(node);
23982 // clean up silly Windows -- stuff?
23985 var lcname = node.tagName.toLowerCase();
23986 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23987 // whitelist of tags..
23989 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23991 node.parentNode.removeChild(node);
23996 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23998 // spans with no attributes - just remove them..
23999 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24000 remove_keep_children = true;
24003 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24004 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24006 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24007 // remove_keep_children = true;
24010 if (remove_keep_children) {
24011 this.cleanUpChildren(node);
24012 // inserts everything just before this node...
24013 while (node.childNodes.length) {
24014 var cn = node.childNodes[0];
24015 node.removeChild(cn);
24016 node.parentNode.insertBefore(cn, node);
24018 node.parentNode.removeChild(node);
24022 if (!node.attributes || !node.attributes.length) {
24027 this.cleanUpChildren(node);
24031 function cleanAttr(n,v)
24034 if (v.match(/^\./) || v.match(/^\//)) {
24037 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24040 if (v.match(/^#/)) {
24043 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24044 node.removeAttribute(n);
24048 var cwhite = this.cwhite;
24049 var cblack = this.cblack;
24051 function cleanStyle(n,v)
24053 if (v.match(/expression/)) { //XSS?? should we even bother..
24054 node.removeAttribute(n);
24058 var parts = v.split(/;/);
24061 Roo.each(parts, function(p) {
24062 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24066 var l = p.split(':').shift().replace(/\s+/g,'');
24067 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24069 if ( cwhite.length && cblack.indexOf(l) > -1) {
24070 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24071 //node.removeAttribute(n);
24075 // only allow 'c whitelisted system attributes'
24076 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24077 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24078 //node.removeAttribute(n);
24088 if (clean.length) {
24089 node.setAttribute(n, clean.join(';'));
24091 node.removeAttribute(n);
24097 for (var i = node.attributes.length-1; i > -1 ; i--) {
24098 var a = node.attributes[i];
24101 if (a.name.toLowerCase().substr(0,2)=='on') {
24102 node.removeAttribute(a.name);
24105 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24106 node.removeAttribute(a.name);
24109 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24110 cleanAttr(a.name,a.value); // fixme..
24113 if (a.name == 'style') {
24114 cleanStyle(a.name,a.value);
24117 /// clean up MS crap..
24118 // tecnically this should be a list of valid class'es..
24121 if (a.name == 'class') {
24122 if (a.value.match(/^Mso/)) {
24123 node.removeAttribute('class');
24126 if (a.value.match(/^body$/)) {
24127 node.removeAttribute('class');
24138 this.cleanUpChildren(node);
24144 * Clean up MS wordisms...
24146 cleanWord : function(node)
24149 this.cleanWord(this.doc.body);
24154 node.nodeName == 'SPAN' &&
24155 !node.hasAttributes() &&
24156 node.childNodes.length == 1 &&
24157 node.firstChild.nodeName == "#text"
24159 var textNode = node.firstChild;
24160 node.removeChild(textNode);
24161 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24162 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24164 node.parentNode.insertBefore(textNode, node);
24165 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24166 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24168 node.parentNode.removeChild(node);
24171 if (node.nodeName == "#text") {
24172 // clean up silly Windows -- stuff?
24175 if (node.nodeName == "#comment") {
24176 node.parentNode.removeChild(node);
24177 // clean up silly Windows -- stuff?
24181 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24182 node.parentNode.removeChild(node);
24185 //Roo.log(node.tagName);
24186 // remove - but keep children..
24187 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24188 //Roo.log('-- removed');
24189 while (node.childNodes.length) {
24190 var cn = node.childNodes[0];
24191 node.removeChild(cn);
24192 node.parentNode.insertBefore(cn, node);
24193 // move node to parent - and clean it..
24194 this.cleanWord(cn);
24196 node.parentNode.removeChild(node);
24197 /// no need to iterate chidlren = it's got none..
24198 //this.iterateChildren(node, this.cleanWord);
24202 if (node.className.length) {
24204 var cn = node.className.split(/\W+/);
24206 Roo.each(cn, function(cls) {
24207 if (cls.match(/Mso[a-zA-Z]+/)) {
24212 node.className = cna.length ? cna.join(' ') : '';
24214 node.removeAttribute("class");
24218 if (node.hasAttribute("lang")) {
24219 node.removeAttribute("lang");
24222 if (node.hasAttribute("style")) {
24224 var styles = node.getAttribute("style").split(";");
24226 Roo.each(styles, function(s) {
24227 if (!s.match(/:/)) {
24230 var kv = s.split(":");
24231 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24234 // what ever is left... we allow.
24237 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24238 if (!nstyle.length) {
24239 node.removeAttribute('style');
24242 this.iterateChildren(node, this.cleanWord);
24248 * iterateChildren of a Node, calling fn each time, using this as the scole..
24249 * @param {DomNode} node node to iterate children of.
24250 * @param {Function} fn method of this class to call on each item.
24252 iterateChildren : function(node, fn)
24254 if (!node.childNodes.length) {
24257 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24258 fn.call(this, node.childNodes[i])
24264 * cleanTableWidths.
24266 * Quite often pasting from word etc.. results in tables with column and widths.
24267 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24270 cleanTableWidths : function(node)
24275 this.cleanTableWidths(this.doc.body);
24280 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24283 Roo.log(node.tagName);
24284 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24285 this.iterateChildren(node, this.cleanTableWidths);
24288 if (node.hasAttribute('width')) {
24289 node.removeAttribute('width');
24293 if (node.hasAttribute("style")) {
24296 var styles = node.getAttribute("style").split(";");
24298 Roo.each(styles, function(s) {
24299 if (!s.match(/:/)) {
24302 var kv = s.split(":");
24303 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24306 // what ever is left... we allow.
24309 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24310 if (!nstyle.length) {
24311 node.removeAttribute('style');
24315 this.iterateChildren(node, this.cleanTableWidths);
24323 domToHTML : function(currentElement, depth, nopadtext) {
24325 depth = depth || 0;
24326 nopadtext = nopadtext || false;
24328 if (!currentElement) {
24329 return this.domToHTML(this.doc.body);
24332 //Roo.log(currentElement);
24334 var allText = false;
24335 var nodeName = currentElement.nodeName;
24336 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24338 if (nodeName == '#text') {
24340 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24345 if (nodeName != 'BODY') {
24348 // Prints the node tagName, such as <A>, <IMG>, etc
24351 for(i = 0; i < currentElement.attributes.length;i++) {
24353 var aname = currentElement.attributes.item(i).name;
24354 if (!currentElement.attributes.item(i).value.length) {
24357 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24360 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24369 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24372 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24377 // Traverse the tree
24379 var currentElementChild = currentElement.childNodes.item(i);
24380 var allText = true;
24381 var innerHTML = '';
24383 while (currentElementChild) {
24384 // Formatting code (indent the tree so it looks nice on the screen)
24385 var nopad = nopadtext;
24386 if (lastnode == 'SPAN') {
24390 if (currentElementChild.nodeName == '#text') {
24391 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24392 toadd = nopadtext ? toadd : toadd.trim();
24393 if (!nopad && toadd.length > 80) {
24394 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
24396 innerHTML += toadd;
24399 currentElementChild = currentElement.childNodes.item(i);
24405 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
24407 // Recursively traverse the tree structure of the child node
24408 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
24409 lastnode = currentElementChild.nodeName;
24411 currentElementChild=currentElement.childNodes.item(i);
24417 // The remaining code is mostly for formatting the tree
24418 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
24423 ret+= "</"+tagName+">";
24429 applyBlacklists : function()
24431 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
24432 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
24436 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24437 if (b.indexOf(tag) > -1) {
24440 this.white.push(tag);
24444 Roo.each(w, function(tag) {
24445 if (b.indexOf(tag) > -1) {
24448 if (this.white.indexOf(tag) > -1) {
24451 this.white.push(tag);
24456 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24457 if (w.indexOf(tag) > -1) {
24460 this.black.push(tag);
24464 Roo.each(b, function(tag) {
24465 if (w.indexOf(tag) > -1) {
24468 if (this.black.indexOf(tag) > -1) {
24471 this.black.push(tag);
24476 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
24477 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
24481 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24482 if (b.indexOf(tag) > -1) {
24485 this.cwhite.push(tag);
24489 Roo.each(w, function(tag) {
24490 if (b.indexOf(tag) > -1) {
24493 if (this.cwhite.indexOf(tag) > -1) {
24496 this.cwhite.push(tag);
24501 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24502 if (w.indexOf(tag) > -1) {
24505 this.cblack.push(tag);
24509 Roo.each(b, function(tag) {
24510 if (w.indexOf(tag) > -1) {
24513 if (this.cblack.indexOf(tag) > -1) {
24516 this.cblack.push(tag);
24521 setStylesheets : function(stylesheets)
24523 if(typeof(stylesheets) == 'string'){
24524 Roo.get(this.iframe.contentDocument.head).createChild({
24526 rel : 'stylesheet',
24535 Roo.each(stylesheets, function(s) {
24540 Roo.get(_this.iframe.contentDocument.head).createChild({
24542 rel : 'stylesheet',
24551 removeStylesheets : function()
24555 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24560 setStyle : function(style)
24562 Roo.get(this.iframe.contentDocument.head).createChild({
24571 // hide stuff that is not compatible
24585 * @event specialkey
24589 * @cfg {String} fieldClass @hide
24592 * @cfg {String} focusClass @hide
24595 * @cfg {String} autoCreate @hide
24598 * @cfg {String} inputType @hide
24601 * @cfg {String} invalidClass @hide
24604 * @cfg {String} invalidText @hide
24607 * @cfg {String} msgFx @hide
24610 * @cfg {String} validateOnBlur @hide
24614 Roo.HtmlEditorCore.white = [
24615 'area', 'br', 'img', 'input', 'hr', 'wbr',
24617 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24618 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24619 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24620 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24621 'table', 'ul', 'xmp',
24623 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24626 'dir', 'menu', 'ol', 'ul', 'dl',
24632 Roo.HtmlEditorCore.black = [
24633 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24635 'base', 'basefont', 'bgsound', 'blink', 'body',
24636 'frame', 'frameset', 'head', 'html', 'ilayer',
24637 'iframe', 'layer', 'link', 'meta', 'object',
24638 'script', 'style' ,'title', 'xml' // clean later..
24640 Roo.HtmlEditorCore.clean = [
24641 'script', 'style', 'title', 'xml'
24643 Roo.HtmlEditorCore.remove = [
24648 Roo.HtmlEditorCore.ablack = [
24652 Roo.HtmlEditorCore.aclean = [
24653 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24657 Roo.HtmlEditorCore.pwhite= [
24658 'http', 'https', 'mailto'
24661 // white listed style attributes.
24662 Roo.HtmlEditorCore.cwhite= [
24663 // 'text-align', /// default is to allow most things..
24669 // black listed style attributes.
24670 Roo.HtmlEditorCore.cblack= [
24671 // 'font-size' -- this can be set by the project
24675 Roo.HtmlEditorCore.swapCodes =[
24694 * @class Roo.bootstrap.HtmlEditor
24695 * @extends Roo.bootstrap.TextArea
24696 * Bootstrap HtmlEditor class
24699 * Create a new HtmlEditor
24700 * @param {Object} config The config object
24703 Roo.bootstrap.HtmlEditor = function(config){
24704 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24705 if (!this.toolbars) {
24706 this.toolbars = [];
24709 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24712 * @event initialize
24713 * Fires when the editor is fully initialized (including the iframe)
24714 * @param {HtmlEditor} this
24719 * Fires when the editor is first receives the focus. Any insertion must wait
24720 * until after this event.
24721 * @param {HtmlEditor} this
24725 * @event beforesync
24726 * Fires before the textarea is updated with content from the editor iframe. Return false
24727 * to cancel the sync.
24728 * @param {HtmlEditor} this
24729 * @param {String} html
24733 * @event beforepush
24734 * Fires before the iframe editor is updated with content from the textarea. Return false
24735 * to cancel the push.
24736 * @param {HtmlEditor} this
24737 * @param {String} html
24742 * Fires when the textarea is updated with content from the editor iframe.
24743 * @param {HtmlEditor} this
24744 * @param {String} html
24749 * Fires when the iframe editor is updated with content from the textarea.
24750 * @param {HtmlEditor} this
24751 * @param {String} html
24755 * @event editmodechange
24756 * Fires when the editor switches edit modes
24757 * @param {HtmlEditor} this
24758 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24760 editmodechange: true,
24762 * @event editorevent
24763 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24764 * @param {HtmlEditor} this
24768 * @event firstfocus
24769 * Fires when on first focus - needed by toolbars..
24770 * @param {HtmlEditor} this
24775 * Auto save the htmlEditor value as a file into Events
24776 * @param {HtmlEditor} this
24780 * @event savedpreview
24781 * preview the saved version of htmlEditor
24782 * @param {HtmlEditor} this
24789 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24793 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24798 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24803 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24808 * @cfg {Number} height (in pixels)
24812 * @cfg {Number} width (in pixels)
24817 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24820 stylesheets: false,
24825 // private properties
24826 validationEvent : false,
24828 initialized : false,
24831 onFocus : Roo.emptyFn,
24833 hideMode:'offsets',
24835 tbContainer : false,
24839 toolbarContainer :function() {
24840 return this.wrap.select('.x-html-editor-tb',true).first();
24844 * Protected method that will not generally be called directly. It
24845 * is called when the editor creates its toolbar. Override this method if you need to
24846 * add custom toolbar buttons.
24847 * @param {HtmlEditor} editor
24849 createToolbar : function(){
24850 Roo.log('renewing');
24851 Roo.log("create toolbars");
24853 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24854 this.toolbars[0].render(this.toolbarContainer());
24858 // if (!editor.toolbars || !editor.toolbars.length) {
24859 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24862 // for (var i =0 ; i < editor.toolbars.length;i++) {
24863 // editor.toolbars[i] = Roo.factory(
24864 // typeof(editor.toolbars[i]) == 'string' ?
24865 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24866 // Roo.bootstrap.HtmlEditor);
24867 // editor.toolbars[i].init(editor);
24873 onRender : function(ct, position)
24875 // Roo.log("Call onRender: " + this.xtype);
24877 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24879 this.wrap = this.inputEl().wrap({
24880 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24883 this.editorcore.onRender(ct, position);
24885 if (this.resizable) {
24886 this.resizeEl = new Roo.Resizable(this.wrap, {
24890 minHeight : this.height,
24891 height: this.height,
24892 handles : this.resizable,
24895 resize : function(r, w, h) {
24896 _t.onResize(w,h); // -something
24902 this.createToolbar(this);
24905 if(!this.width && this.resizable){
24906 this.setSize(this.wrap.getSize());
24908 if (this.resizeEl) {
24909 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24910 // should trigger onReize..
24916 onResize : function(w, h)
24918 Roo.log('resize: ' +w + ',' + h );
24919 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24923 if(this.inputEl() ){
24924 if(typeof w == 'number'){
24925 var aw = w - this.wrap.getFrameWidth('lr');
24926 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24929 if(typeof h == 'number'){
24930 var tbh = -11; // fixme it needs to tool bar size!
24931 for (var i =0; i < this.toolbars.length;i++) {
24932 // fixme - ask toolbars for heights?
24933 tbh += this.toolbars[i].el.getHeight();
24934 //if (this.toolbars[i].footer) {
24935 // tbh += this.toolbars[i].footer.el.getHeight();
24943 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24944 ah -= 5; // knock a few pixes off for look..
24945 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24949 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24950 this.editorcore.onResize(ew,eh);
24955 * Toggles the editor between standard and source edit mode.
24956 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24958 toggleSourceEdit : function(sourceEditMode)
24960 this.editorcore.toggleSourceEdit(sourceEditMode);
24962 if(this.editorcore.sourceEditMode){
24963 Roo.log('editor - showing textarea');
24966 // Roo.log(this.syncValue());
24968 this.inputEl().removeClass(['hide', 'x-hidden']);
24969 this.inputEl().dom.removeAttribute('tabIndex');
24970 this.inputEl().focus();
24972 Roo.log('editor - hiding textarea');
24974 // Roo.log(this.pushValue());
24977 this.inputEl().addClass(['hide', 'x-hidden']);
24978 this.inputEl().dom.setAttribute('tabIndex', -1);
24979 //this.deferFocus();
24982 if(this.resizable){
24983 this.setSize(this.wrap.getSize());
24986 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24989 // private (for BoxComponent)
24990 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24992 // private (for BoxComponent)
24993 getResizeEl : function(){
24997 // private (for BoxComponent)
24998 getPositionEl : function(){
25003 initEvents : function(){
25004 this.originalValue = this.getValue();
25008 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25011 // markInvalid : Roo.emptyFn,
25013 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25016 // clearInvalid : Roo.emptyFn,
25018 setValue : function(v){
25019 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25020 this.editorcore.pushValue();
25025 deferFocus : function(){
25026 this.focus.defer(10, this);
25030 focus : function(){
25031 this.editorcore.focus();
25037 onDestroy : function(){
25043 for (var i =0; i < this.toolbars.length;i++) {
25044 // fixme - ask toolbars for heights?
25045 this.toolbars[i].onDestroy();
25048 this.wrap.dom.innerHTML = '';
25049 this.wrap.remove();
25054 onFirstFocus : function(){
25055 //Roo.log("onFirstFocus");
25056 this.editorcore.onFirstFocus();
25057 for (var i =0; i < this.toolbars.length;i++) {
25058 this.toolbars[i].onFirstFocus();
25064 syncValue : function()
25066 this.editorcore.syncValue();
25069 pushValue : function()
25071 this.editorcore.pushValue();
25075 // hide stuff that is not compatible
25089 * @event specialkey
25093 * @cfg {String} fieldClass @hide
25096 * @cfg {String} focusClass @hide
25099 * @cfg {String} autoCreate @hide
25102 * @cfg {String} inputType @hide
25106 * @cfg {String} invalidText @hide
25109 * @cfg {String} msgFx @hide
25112 * @cfg {String} validateOnBlur @hide
25121 Roo.namespace('Roo.bootstrap.htmleditor');
25123 * @class Roo.bootstrap.HtmlEditorToolbar1
25129 new Roo.bootstrap.HtmlEditor({
25132 new Roo.bootstrap.HtmlEditorToolbar1({
25133 disable : { fonts: 1 , format: 1, ..., ... , ...],
25139 * @cfg {Object} disable List of elements to disable..
25140 * @cfg {Array} btns List of additional buttons.
25144 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25147 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25150 Roo.apply(this, config);
25152 // default disabled, based on 'good practice'..
25153 this.disable = this.disable || {};
25154 Roo.applyIf(this.disable, {
25157 specialElements : true
25159 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25161 this.editor = config.editor;
25162 this.editorcore = config.editor.editorcore;
25164 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25166 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25167 // dont call parent... till later.
25169 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25174 editorcore : false,
25179 "h1","h2","h3","h4","h5","h6",
25181 "abbr", "acronym", "address", "cite", "samp", "var",
25185 onRender : function(ct, position)
25187 // Roo.log("Call onRender: " + this.xtype);
25189 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25191 this.el.dom.style.marginBottom = '0';
25193 var editorcore = this.editorcore;
25194 var editor= this.editor;
25197 var btn = function(id,cmd , toggle, handler, html){
25199 var event = toggle ? 'toggle' : 'click';
25204 xns: Roo.bootstrap,
25208 enableToggle:toggle !== false,
25210 pressed : toggle ? false : null,
25213 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25214 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25220 // var cb_box = function...
25225 xns: Roo.bootstrap,
25230 xns: Roo.bootstrap,
25234 Roo.each(this.formats, function(f) {
25235 style.menu.items.push({
25237 xns: Roo.bootstrap,
25238 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25243 editorcore.insertTag(this.tagname);
25250 children.push(style);
25252 btn('bold',false,true);
25253 btn('italic',false,true);
25254 btn('align-left', 'justifyleft',true);
25255 btn('align-center', 'justifycenter',true);
25256 btn('align-right' , 'justifyright',true);
25257 btn('link', false, false, function(btn) {
25258 //Roo.log("create link?");
25259 var url = prompt(this.createLinkText, this.defaultLinkValue);
25260 if(url && url != 'http:/'+'/'){
25261 this.editorcore.relayCmd('createlink', url);
25264 btn('list','insertunorderedlist',true);
25265 btn('pencil', false,true, function(btn){
25267 this.toggleSourceEdit(btn.pressed);
25270 if (this.editor.btns.length > 0) {
25271 for (var i = 0; i<this.editor.btns.length; i++) {
25272 children.push(this.editor.btns[i]);
25280 xns: Roo.bootstrap,
25285 xns: Roo.bootstrap,
25290 cog.menu.items.push({
25292 xns: Roo.bootstrap,
25293 html : Clean styles,
25298 editorcore.insertTag(this.tagname);
25307 this.xtype = 'NavSimplebar';
25309 for(var i=0;i< children.length;i++) {
25311 this.buttons.add(this.addxtypeChild(children[i]));
25315 editor.on('editorevent', this.updateToolbar, this);
25317 onBtnClick : function(id)
25319 this.editorcore.relayCmd(id);
25320 this.editorcore.focus();
25324 * Protected method that will not generally be called directly. It triggers
25325 * a toolbar update by reading the markup state of the current selection in the editor.
25327 updateToolbar: function(){
25329 if(!this.editorcore.activated){
25330 this.editor.onFirstFocus(); // is this neeed?
25334 var btns = this.buttons;
25335 var doc = this.editorcore.doc;
25336 btns.get('bold').setActive(doc.queryCommandState('bold'));
25337 btns.get('italic').setActive(doc.queryCommandState('italic'));
25338 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25340 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25341 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25342 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25344 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25345 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25348 var ans = this.editorcore.getAllAncestors();
25349 if (this.formatCombo) {
25352 var store = this.formatCombo.store;
25353 this.formatCombo.setValue("");
25354 for (var i =0; i < ans.length;i++) {
25355 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25357 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25365 // hides menus... - so this cant be on a menu...
25366 Roo.bootstrap.MenuMgr.hideAll();
25368 Roo.bootstrap.MenuMgr.hideAll();
25369 //this.editorsyncValue();
25371 onFirstFocus: function() {
25372 this.buttons.each(function(item){
25376 toggleSourceEdit : function(sourceEditMode){
25379 if(sourceEditMode){
25380 Roo.log("disabling buttons");
25381 this.buttons.each( function(item){
25382 if(item.cmd != 'pencil'){
25388 Roo.log("enabling buttons");
25389 if(this.editorcore.initialized){
25390 this.buttons.each( function(item){
25396 Roo.log("calling toggole on editor");
25397 // tell the editor that it's been pressed..
25398 this.editor.toggleSourceEdit(sourceEditMode);
25408 * @class Roo.bootstrap.Table.AbstractSelectionModel
25409 * @extends Roo.util.Observable
25410 * Abstract base class for grid SelectionModels. It provides the interface that should be
25411 * implemented by descendant classes. This class should not be directly instantiated.
25414 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25415 this.locked = false;
25416 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25420 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
25421 /** @ignore Called by the grid automatically. Do not call directly. */
25422 init : function(grid){
25428 * Locks the selections.
25431 this.locked = true;
25435 * Unlocks the selections.
25437 unlock : function(){
25438 this.locked = false;
25442 * Returns true if the selections are locked.
25443 * @return {Boolean}
25445 isLocked : function(){
25446 return this.locked;
25450 initEvents : function ()
25456 * @extends Roo.bootstrap.Table.AbstractSelectionModel
25457 * @class Roo.bootstrap.Table.RowSelectionModel
25458 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25459 * It supports multiple selections and keyboard selection/navigation.
25461 * @param {Object} config
25464 Roo.bootstrap.Table.RowSelectionModel = function(config){
25465 Roo.apply(this, config);
25466 this.selections = new Roo.util.MixedCollection(false, function(o){
25471 this.lastActive = false;
25475 * @event selectionchange
25476 * Fires when the selection changes
25477 * @param {SelectionModel} this
25479 "selectionchange" : true,
25481 * @event afterselectionchange
25482 * Fires after the selection changes (eg. by key press or clicking)
25483 * @param {SelectionModel} this
25485 "afterselectionchange" : true,
25487 * @event beforerowselect
25488 * Fires when a row is selected being selected, return false to cancel.
25489 * @param {SelectionModel} this
25490 * @param {Number} rowIndex The selected index
25491 * @param {Boolean} keepExisting False if other selections will be cleared
25493 "beforerowselect" : true,
25496 * Fires when a row is selected.
25497 * @param {SelectionModel} this
25498 * @param {Number} rowIndex The selected index
25499 * @param {Roo.data.Record} r The record
25501 "rowselect" : true,
25503 * @event rowdeselect
25504 * Fires when a row is deselected.
25505 * @param {SelectionModel} this
25506 * @param {Number} rowIndex The selected index
25508 "rowdeselect" : true
25510 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25511 this.locked = false;
25514 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
25516 * @cfg {Boolean} singleSelect
25517 * True to allow selection of only one row at a time (defaults to false)
25519 singleSelect : false,
25522 initEvents : function()
25525 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25526 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
25527 //}else{ // allow click to work like normal
25528 // this.grid.on("rowclick", this.handleDragableRowClick, this);
25530 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25531 this.grid.on("rowclick", this.handleMouseDown, this);
25533 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25534 "up" : function(e){
25536 this.selectPrevious(e.shiftKey);
25537 }else if(this.last !== false && this.lastActive !== false){
25538 var last = this.last;
25539 this.selectRange(this.last, this.lastActive-1);
25540 this.grid.getView().focusRow(this.lastActive);
25541 if(last !== false){
25545 this.selectFirstRow();
25547 this.fireEvent("afterselectionchange", this);
25549 "down" : function(e){
25551 this.selectNext(e.shiftKey);
25552 }else if(this.last !== false && this.lastActive !== false){
25553 var last = this.last;
25554 this.selectRange(this.last, this.lastActive+1);
25555 this.grid.getView().focusRow(this.lastActive);
25556 if(last !== false){
25560 this.selectFirstRow();
25562 this.fireEvent("afterselectionchange", this);
25566 this.grid.store.on('load', function(){
25567 this.selections.clear();
25570 var view = this.grid.view;
25571 view.on("refresh", this.onRefresh, this);
25572 view.on("rowupdated", this.onRowUpdated, this);
25573 view.on("rowremoved", this.onRemove, this);
25578 onRefresh : function()
25580 var ds = this.grid.store, i, v = this.grid.view;
25581 var s = this.selections;
25582 s.each(function(r){
25583 if((i = ds.indexOfId(r.id)) != -1){
25592 onRemove : function(v, index, r){
25593 this.selections.remove(r);
25597 onRowUpdated : function(v, index, r){
25598 if(this.isSelected(r)){
25599 v.onRowSelect(index);
25605 * @param {Array} records The records to select
25606 * @param {Boolean} keepExisting (optional) True to keep existing selections
25608 selectRecords : function(records, keepExisting)
25611 this.clearSelections();
25613 var ds = this.grid.store;
25614 for(var i = 0, len = records.length; i < len; i++){
25615 this.selectRow(ds.indexOf(records[i]), true);
25620 * Gets the number of selected rows.
25623 getCount : function(){
25624 return this.selections.length;
25628 * Selects the first row in the grid.
25630 selectFirstRow : function(){
25635 * Select the last row.
25636 * @param {Boolean} keepExisting (optional) True to keep existing selections
25638 selectLastRow : function(keepExisting){
25639 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25640 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25644 * Selects the row immediately following the last selected row.
25645 * @param {Boolean} keepExisting (optional) True to keep existing selections
25647 selectNext : function(keepExisting)
25649 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25650 this.selectRow(this.last+1, keepExisting);
25651 this.grid.getView().focusRow(this.last);
25656 * Selects the row that precedes the last selected row.
25657 * @param {Boolean} keepExisting (optional) True to keep existing selections
25659 selectPrevious : function(keepExisting){
25661 this.selectRow(this.last-1, keepExisting);
25662 this.grid.getView().focusRow(this.last);
25667 * Returns the selected records
25668 * @return {Array} Array of selected records
25670 getSelections : function(){
25671 return [].concat(this.selections.items);
25675 * Returns the first selected record.
25678 getSelected : function(){
25679 return this.selections.itemAt(0);
25684 * Clears all selections.
25686 clearSelections : function(fast)
25692 var ds = this.grid.store;
25693 var s = this.selections;
25694 s.each(function(r){
25695 this.deselectRow(ds.indexOfId(r.id));
25699 this.selections.clear();
25706 * Selects all rows.
25708 selectAll : function(){
25712 this.selections.clear();
25713 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25714 this.selectRow(i, true);
25719 * Returns True if there is a selection.
25720 * @return {Boolean}
25722 hasSelection : function(){
25723 return this.selections.length > 0;
25727 * Returns True if the specified row is selected.
25728 * @param {Number/Record} record The record or index of the record to check
25729 * @return {Boolean}
25731 isSelected : function(index){
25732 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25733 return (r && this.selections.key(r.id) ? true : false);
25737 * Returns True if the specified record id is selected.
25738 * @param {String} id The id of record to check
25739 * @return {Boolean}
25741 isIdSelected : function(id){
25742 return (this.selections.key(id) ? true : false);
25747 handleMouseDBClick : function(e, t){
25751 handleMouseDown : function(e, t)
25753 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25754 if(this.isLocked() || rowIndex < 0 ){
25757 if(e.shiftKey && this.last !== false){
25758 var last = this.last;
25759 this.selectRange(last, rowIndex, e.ctrlKey);
25760 this.last = last; // reset the last
25764 var isSelected = this.isSelected(rowIndex);
25765 //Roo.log("select row:" + rowIndex);
25767 this.deselectRow(rowIndex);
25769 this.selectRow(rowIndex, true);
25773 if(e.button !== 0 && isSelected){
25774 alert('rowIndex 2: ' + rowIndex);
25775 view.focusRow(rowIndex);
25776 }else if(e.ctrlKey && isSelected){
25777 this.deselectRow(rowIndex);
25778 }else if(!isSelected){
25779 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25780 view.focusRow(rowIndex);
25784 this.fireEvent("afterselectionchange", this);
25787 handleDragableRowClick : function(grid, rowIndex, e)
25789 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25790 this.selectRow(rowIndex, false);
25791 grid.view.focusRow(rowIndex);
25792 this.fireEvent("afterselectionchange", this);
25797 * Selects multiple rows.
25798 * @param {Array} rows Array of the indexes of the row to select
25799 * @param {Boolean} keepExisting (optional) True to keep existing selections
25801 selectRows : function(rows, keepExisting){
25803 this.clearSelections();
25805 for(var i = 0, len = rows.length; i < len; i++){
25806 this.selectRow(rows[i], true);
25811 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25812 * @param {Number} startRow The index of the first row in the range
25813 * @param {Number} endRow The index of the last row in the range
25814 * @param {Boolean} keepExisting (optional) True to retain existing selections
25816 selectRange : function(startRow, endRow, keepExisting){
25821 this.clearSelections();
25823 if(startRow <= endRow){
25824 for(var i = startRow; i <= endRow; i++){
25825 this.selectRow(i, true);
25828 for(var i = startRow; i >= endRow; i--){
25829 this.selectRow(i, true);
25835 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25836 * @param {Number} startRow The index of the first row in the range
25837 * @param {Number} endRow The index of the last row in the range
25839 deselectRange : function(startRow, endRow, preventViewNotify){
25843 for(var i = startRow; i <= endRow; i++){
25844 this.deselectRow(i, preventViewNotify);
25850 * @param {Number} row The index of the row to select
25851 * @param {Boolean} keepExisting (optional) True to keep existing selections
25853 selectRow : function(index, keepExisting, preventViewNotify)
25855 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25858 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25859 if(!keepExisting || this.singleSelect){
25860 this.clearSelections();
25863 var r = this.grid.store.getAt(index);
25864 //console.log('selectRow - record id :' + r.id);
25866 this.selections.add(r);
25867 this.last = this.lastActive = index;
25868 if(!preventViewNotify){
25869 var proxy = new Roo.Element(
25870 this.grid.getRowDom(index)
25872 proxy.addClass('bg-info info');
25874 this.fireEvent("rowselect", this, index, r);
25875 this.fireEvent("selectionchange", this);
25881 * @param {Number} row The index of the row to deselect
25883 deselectRow : function(index, preventViewNotify)
25888 if(this.last == index){
25891 if(this.lastActive == index){
25892 this.lastActive = false;
25895 var r = this.grid.store.getAt(index);
25900 this.selections.remove(r);
25901 //.console.log('deselectRow - record id :' + r.id);
25902 if(!preventViewNotify){
25904 var proxy = new Roo.Element(
25905 this.grid.getRowDom(index)
25907 proxy.removeClass('bg-info info');
25909 this.fireEvent("rowdeselect", this, index);
25910 this.fireEvent("selectionchange", this);
25914 restoreLast : function(){
25916 this.last = this._last;
25921 acceptsNav : function(row, col, cm){
25922 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25926 onEditorKey : function(field, e){
25927 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25932 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25934 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25936 }else if(k == e.ENTER && !e.ctrlKey){
25940 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25942 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25944 }else if(k == e.ESC){
25948 g.startEditing(newCell[0], newCell[1]);
25954 * Ext JS Library 1.1.1
25955 * Copyright(c) 2006-2007, Ext JS, LLC.
25957 * Originally Released Under LGPL - original licence link has changed is not relivant.
25960 * <script type="text/javascript">
25964 * @class Roo.bootstrap.PagingToolbar
25965 * @extends Roo.bootstrap.NavSimplebar
25966 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25968 * Create a new PagingToolbar
25969 * @param {Object} config The config object
25970 * @param {Roo.data.Store} store
25972 Roo.bootstrap.PagingToolbar = function(config)
25974 // old args format still supported... - xtype is prefered..
25975 // created from xtype...
25977 this.ds = config.dataSource;
25979 if (config.store && !this.ds) {
25980 this.store= Roo.factory(config.store, Roo.data);
25981 this.ds = this.store;
25982 this.ds.xmodule = this.xmodule || false;
25985 this.toolbarItems = [];
25986 if (config.items) {
25987 this.toolbarItems = config.items;
25990 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25995 this.bind(this.ds);
25998 if (Roo.bootstrap.version == 4) {
25999 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26001 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26006 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26008 * @cfg {Roo.data.Store} dataSource
26009 * The underlying data store providing the paged data
26012 * @cfg {String/HTMLElement/Element} container
26013 * container The id or element that will contain the toolbar
26016 * @cfg {Boolean} displayInfo
26017 * True to display the displayMsg (defaults to false)
26020 * @cfg {Number} pageSize
26021 * The number of records to display per page (defaults to 20)
26025 * @cfg {String} displayMsg
26026 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26028 displayMsg : 'Displaying {0} - {1} of {2}',
26030 * @cfg {String} emptyMsg
26031 * The message to display when no records are found (defaults to "No data to display")
26033 emptyMsg : 'No data to display',
26035 * Customizable piece of the default paging text (defaults to "Page")
26038 beforePageText : "Page",
26040 * Customizable piece of the default paging text (defaults to "of %0")
26043 afterPageText : "of {0}",
26045 * Customizable piece of the default paging text (defaults to "First Page")
26048 firstText : "First Page",
26050 * Customizable piece of the default paging text (defaults to "Previous Page")
26053 prevText : "Previous Page",
26055 * Customizable piece of the default paging text (defaults to "Next Page")
26058 nextText : "Next Page",
26060 * Customizable piece of the default paging text (defaults to "Last Page")
26063 lastText : "Last Page",
26065 * Customizable piece of the default paging text (defaults to "Refresh")
26068 refreshText : "Refresh",
26072 onRender : function(ct, position)
26074 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26075 this.navgroup.parentId = this.id;
26076 this.navgroup.onRender(this.el, null);
26077 // add the buttons to the navgroup
26079 if(this.displayInfo){
26080 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26081 this.displayEl = this.el.select('.x-paging-info', true).first();
26082 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26083 // this.displayEl = navel.el.select('span',true).first();
26089 Roo.each(_this.buttons, function(e){ // this might need to use render????
26090 Roo.factory(e).render(_this.el);
26094 Roo.each(_this.toolbarItems, function(e) {
26095 _this.navgroup.addItem(e);
26099 this.first = this.navgroup.addItem({
26100 tooltip: this.firstText,
26101 cls: "prev btn-outline-secondary",
26102 html : ' <i class="fa fa-step-backward"></i>',
26104 preventDefault: true,
26105 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26108 this.prev = this.navgroup.addItem({
26109 tooltip: this.prevText,
26110 cls: "prev btn-outline-secondary",
26111 html : ' <i class="fa fa-backward"></i>',
26113 preventDefault: true,
26114 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26116 //this.addSeparator();
26119 var field = this.navgroup.addItem( {
26121 cls : 'x-paging-position btn-outline-secondary',
26123 html : this.beforePageText +
26124 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26125 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26128 this.field = field.el.select('input', true).first();
26129 this.field.on("keydown", this.onPagingKeydown, this);
26130 this.field.on("focus", function(){this.dom.select();});
26133 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26134 //this.field.setHeight(18);
26135 //this.addSeparator();
26136 this.next = this.navgroup.addItem({
26137 tooltip: this.nextText,
26138 cls: "next btn-outline-secondary",
26139 html : ' <i class="fa fa-forward"></i>',
26141 preventDefault: true,
26142 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26144 this.last = this.navgroup.addItem({
26145 tooltip: this.lastText,
26146 html : ' <i class="fa fa-step-forward"></i>',
26147 cls: "next btn-outline-secondary",
26149 preventDefault: true,
26150 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26152 //this.addSeparator();
26153 this.loading = this.navgroup.addItem({
26154 tooltip: this.refreshText,
26155 cls: "btn-outline-secondary",
26156 html : ' <i class="fa fa-refresh"></i>',
26157 preventDefault: true,
26158 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26164 updateInfo : function(){
26165 if(this.displayEl){
26166 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26167 var msg = count == 0 ?
26171 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26173 this.displayEl.update(msg);
26178 onLoad : function(ds, r, o)
26180 this.cursor = o.params.start ? o.params.start : 0;
26182 var d = this.getPageData(),
26187 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26188 this.field.dom.value = ap;
26189 this.first.setDisabled(ap == 1);
26190 this.prev.setDisabled(ap == 1);
26191 this.next.setDisabled(ap == ps);
26192 this.last.setDisabled(ap == ps);
26193 this.loading.enable();
26198 getPageData : function(){
26199 var total = this.ds.getTotalCount();
26202 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26203 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26208 onLoadError : function(){
26209 this.loading.enable();
26213 onPagingKeydown : function(e){
26214 var k = e.getKey();
26215 var d = this.getPageData();
26217 var v = this.field.dom.value, pageNum;
26218 if(!v || isNaN(pageNum = parseInt(v, 10))){
26219 this.field.dom.value = d.activePage;
26222 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26223 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26226 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))
26228 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26229 this.field.dom.value = pageNum;
26230 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26233 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26235 var v = this.field.dom.value, pageNum;
26236 var increment = (e.shiftKey) ? 10 : 1;
26237 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26240 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26241 this.field.dom.value = d.activePage;
26244 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26246 this.field.dom.value = parseInt(v, 10) + increment;
26247 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26248 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26255 beforeLoad : function(){
26257 this.loading.disable();
26262 onClick : function(which){
26271 ds.load({params:{start: 0, limit: this.pageSize}});
26274 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26277 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26280 var total = ds.getTotalCount();
26281 var extra = total % this.pageSize;
26282 var lastStart = extra ? (total - extra) : total-this.pageSize;
26283 ds.load({params:{start: lastStart, limit: this.pageSize}});
26286 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26292 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26293 * @param {Roo.data.Store} store The data store to unbind
26295 unbind : function(ds){
26296 ds.un("beforeload", this.beforeLoad, this);
26297 ds.un("load", this.onLoad, this);
26298 ds.un("loadexception", this.onLoadError, this);
26299 ds.un("remove", this.updateInfo, this);
26300 ds.un("add", this.updateInfo, this);
26301 this.ds = undefined;
26305 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26306 * @param {Roo.data.Store} store The data store to bind
26308 bind : function(ds){
26309 ds.on("beforeload", this.beforeLoad, this);
26310 ds.on("load", this.onLoad, this);
26311 ds.on("loadexception", this.onLoadError, this);
26312 ds.on("remove", this.updateInfo, this);
26313 ds.on("add", this.updateInfo, this);
26324 * @class Roo.bootstrap.MessageBar
26325 * @extends Roo.bootstrap.Component
26326 * Bootstrap MessageBar class
26327 * @cfg {String} html contents of the MessageBar
26328 * @cfg {String} weight (info | success | warning | danger) default info
26329 * @cfg {String} beforeClass insert the bar before the given class
26330 * @cfg {Boolean} closable (true | false) default false
26331 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26334 * Create a new Element
26335 * @param {Object} config The config object
26338 Roo.bootstrap.MessageBar = function(config){
26339 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26342 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26348 beforeClass: 'bootstrap-sticky-wrap',
26350 getAutoCreate : function(){
26354 cls: 'alert alert-dismissable alert-' + this.weight,
26359 html: this.html || ''
26365 cfg.cls += ' alert-messages-fixed';
26379 onRender : function(ct, position)
26381 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26384 var cfg = Roo.apply({}, this.getAutoCreate());
26388 cfg.cls += ' ' + this.cls;
26391 cfg.style = this.style;
26393 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26395 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26398 this.el.select('>button.close').on('click', this.hide, this);
26404 if (!this.rendered) {
26410 this.fireEvent('show', this);
26416 if (!this.rendered) {
26422 this.fireEvent('hide', this);
26425 update : function()
26427 // var e = this.el.dom.firstChild;
26429 // if(this.closable){
26430 // e = e.nextSibling;
26433 // e.data = this.html || '';
26435 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26451 * @class Roo.bootstrap.Graph
26452 * @extends Roo.bootstrap.Component
26453 * Bootstrap Graph class
26457 @cfg {String} graphtype bar | vbar | pie
26458 @cfg {number} g_x coodinator | centre x (pie)
26459 @cfg {number} g_y coodinator | centre y (pie)
26460 @cfg {number} g_r radius (pie)
26461 @cfg {number} g_height height of the chart (respected by all elements in the set)
26462 @cfg {number} g_width width of the chart (respected by all elements in the set)
26463 @cfg {Object} title The title of the chart
26466 -opts (object) options for the chart
26468 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26469 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26471 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.
26472 o stacked (boolean) whether or not to tread values as in a stacked bar chart
26474 o stretch (boolean)
26476 -opts (object) options for the pie
26479 o startAngle (number)
26480 o endAngle (number)
26484 * Create a new Input
26485 * @param {Object} config The config object
26488 Roo.bootstrap.Graph = function(config){
26489 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26495 * The img click event for the img.
26496 * @param {Roo.EventObject} e
26502 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
26513 //g_colors: this.colors,
26520 getAutoCreate : function(){
26531 onRender : function(ct,position){
26534 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26536 if (typeof(Raphael) == 'undefined') {
26537 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26541 this.raphael = Raphael(this.el.dom);
26543 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26544 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26545 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26546 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26548 r.text(160, 10, "Single Series Chart").attr(txtattr);
26549 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26550 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26551 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26553 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26554 r.barchart(330, 10, 300, 220, data1);
26555 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26556 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26559 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26560 // r.barchart(30, 30, 560, 250, xdata, {
26561 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26562 // axis : "0 0 1 1",
26563 // axisxlabels : xdata
26564 // //yvalues : cols,
26567 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26569 // this.load(null,xdata,{
26570 // axis : "0 0 1 1",
26571 // axisxlabels : xdata
26576 load : function(graphtype,xdata,opts)
26578 this.raphael.clear();
26580 graphtype = this.graphtype;
26585 var r = this.raphael,
26586 fin = function () {
26587 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26589 fout = function () {
26590 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26592 pfin = function() {
26593 this.sector.stop();
26594 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26597 this.label[0].stop();
26598 this.label[0].attr({ r: 7.5 });
26599 this.label[1].attr({ "font-weight": 800 });
26602 pfout = function() {
26603 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26606 this.label[0].animate({ r: 5 }, 500, "bounce");
26607 this.label[1].attr({ "font-weight": 400 });
26613 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26616 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26619 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26620 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26622 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26629 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26634 setTitle: function(o)
26639 initEvents: function() {
26642 this.el.on('click', this.onClick, this);
26646 onClick : function(e)
26648 Roo.log('img onclick');
26649 this.fireEvent('click', this, e);
26661 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26664 * @class Roo.bootstrap.dash.NumberBox
26665 * @extends Roo.bootstrap.Component
26666 * Bootstrap NumberBox class
26667 * @cfg {String} headline Box headline
26668 * @cfg {String} content Box content
26669 * @cfg {String} icon Box icon
26670 * @cfg {String} footer Footer text
26671 * @cfg {String} fhref Footer href
26674 * Create a new NumberBox
26675 * @param {Object} config The config object
26679 Roo.bootstrap.dash.NumberBox = function(config){
26680 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26684 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26693 getAutoCreate : function(){
26697 cls : 'small-box ',
26705 cls : 'roo-headline',
26706 html : this.headline
26710 cls : 'roo-content',
26711 html : this.content
26725 cls : 'ion ' + this.icon
26734 cls : 'small-box-footer',
26735 href : this.fhref || '#',
26739 cfg.cn.push(footer);
26746 onRender : function(ct,position){
26747 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26754 setHeadline: function (value)
26756 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26759 setFooter: function (value, href)
26761 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26764 this.el.select('a.small-box-footer',true).first().attr('href', href);
26769 setContent: function (value)
26771 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26774 initEvents: function()
26788 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26791 * @class Roo.bootstrap.dash.TabBox
26792 * @extends Roo.bootstrap.Component
26793 * Bootstrap TabBox class
26794 * @cfg {String} title Title of the TabBox
26795 * @cfg {String} icon Icon of the TabBox
26796 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26797 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26800 * Create a new TabBox
26801 * @param {Object} config The config object
26805 Roo.bootstrap.dash.TabBox = function(config){
26806 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26811 * When a pane is added
26812 * @param {Roo.bootstrap.dash.TabPane} pane
26816 * @event activatepane
26817 * When a pane is activated
26818 * @param {Roo.bootstrap.dash.TabPane} pane
26820 "activatepane" : true
26828 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26833 tabScrollable : false,
26835 getChildContainer : function()
26837 return this.el.select('.tab-content', true).first();
26840 getAutoCreate : function(){
26844 cls: 'pull-left header',
26852 cls: 'fa ' + this.icon
26858 cls: 'nav nav-tabs pull-right',
26864 if(this.tabScrollable){
26871 cls: 'nav nav-tabs pull-right',
26882 cls: 'nav-tabs-custom',
26887 cls: 'tab-content no-padding',
26895 initEvents : function()
26897 //Roo.log('add add pane handler');
26898 this.on('addpane', this.onAddPane, this);
26901 * Updates the box title
26902 * @param {String} html to set the title to.
26904 setTitle : function(value)
26906 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26908 onAddPane : function(pane)
26910 this.panes.push(pane);
26911 //Roo.log('addpane');
26913 // tabs are rendere left to right..
26914 if(!this.showtabs){
26918 var ctr = this.el.select('.nav-tabs', true).first();
26921 var existing = ctr.select('.nav-tab',true);
26922 var qty = existing.getCount();;
26925 var tab = ctr.createChild({
26927 cls : 'nav-tab' + (qty ? '' : ' active'),
26935 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26938 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26940 pane.el.addClass('active');
26945 onTabClick : function(ev,un,ob,pane)
26947 //Roo.log('tab - prev default');
26948 ev.preventDefault();
26951 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26952 pane.tab.addClass('active');
26953 //Roo.log(pane.title);
26954 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26955 // technically we should have a deactivate event.. but maybe add later.
26956 // and it should not de-activate the selected tab...
26957 this.fireEvent('activatepane', pane);
26958 pane.el.addClass('active');
26959 pane.fireEvent('activate');
26964 getActivePane : function()
26967 Roo.each(this.panes, function(p) {
26968 if(p.el.hasClass('active')){
26989 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26991 * @class Roo.bootstrap.TabPane
26992 * @extends Roo.bootstrap.Component
26993 * Bootstrap TabPane class
26994 * @cfg {Boolean} active (false | true) Default false
26995 * @cfg {String} title title of panel
26999 * Create a new TabPane
27000 * @param {Object} config The config object
27003 Roo.bootstrap.dash.TabPane = function(config){
27004 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27010 * When a pane is activated
27011 * @param {Roo.bootstrap.dash.TabPane} pane
27018 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27023 // the tabBox that this is attached to.
27026 getAutoCreate : function()
27034 cfg.cls += ' active';
27039 initEvents : function()
27041 //Roo.log('trigger add pane handler');
27042 this.parent().fireEvent('addpane', this)
27046 * Updates the tab title
27047 * @param {String} html to set the title to.
27049 setTitle: function(str)
27055 this.tab.select('a', true).first().dom.innerHTML = str;
27072 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27075 * @class Roo.bootstrap.menu.Menu
27076 * @extends Roo.bootstrap.Component
27077 * Bootstrap Menu class - container for Menu
27078 * @cfg {String} html Text of the menu
27079 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27080 * @cfg {String} icon Font awesome icon
27081 * @cfg {String} pos Menu align to (top | bottom) default bottom
27085 * Create a new Menu
27086 * @param {Object} config The config object
27090 Roo.bootstrap.menu.Menu = function(config){
27091 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27095 * @event beforeshow
27096 * Fires before this menu is displayed
27097 * @param {Roo.bootstrap.menu.Menu} this
27101 * @event beforehide
27102 * Fires before this menu is hidden
27103 * @param {Roo.bootstrap.menu.Menu} this
27108 * Fires after this menu is displayed
27109 * @param {Roo.bootstrap.menu.Menu} this
27114 * Fires after this menu is hidden
27115 * @param {Roo.bootstrap.menu.Menu} this
27120 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27121 * @param {Roo.bootstrap.menu.Menu} this
27122 * @param {Roo.EventObject} e
27129 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27133 weight : 'default',
27138 getChildContainer : function() {
27139 if(this.isSubMenu){
27143 return this.el.select('ul.dropdown-menu', true).first();
27146 getAutoCreate : function()
27151 cls : 'roo-menu-text',
27159 cls : 'fa ' + this.icon
27170 cls : 'dropdown-button btn btn-' + this.weight,
27175 cls : 'dropdown-toggle btn btn-' + this.weight,
27185 cls : 'dropdown-menu'
27191 if(this.pos == 'top'){
27192 cfg.cls += ' dropup';
27195 if(this.isSubMenu){
27198 cls : 'dropdown-menu'
27205 onRender : function(ct, position)
27207 this.isSubMenu = ct.hasClass('dropdown-submenu');
27209 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27212 initEvents : function()
27214 if(this.isSubMenu){
27218 this.hidden = true;
27220 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27221 this.triggerEl.on('click', this.onTriggerPress, this);
27223 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27224 this.buttonEl.on('click', this.onClick, this);
27230 if(this.isSubMenu){
27234 return this.el.select('ul.dropdown-menu', true).first();
27237 onClick : function(e)
27239 this.fireEvent("click", this, e);
27242 onTriggerPress : function(e)
27244 if (this.isVisible()) {
27251 isVisible : function(){
27252 return !this.hidden;
27257 this.fireEvent("beforeshow", this);
27259 this.hidden = false;
27260 this.el.addClass('open');
27262 Roo.get(document).on("mouseup", this.onMouseUp, this);
27264 this.fireEvent("show", this);
27271 this.fireEvent("beforehide", this);
27273 this.hidden = true;
27274 this.el.removeClass('open');
27276 Roo.get(document).un("mouseup", this.onMouseUp);
27278 this.fireEvent("hide", this);
27281 onMouseUp : function()
27295 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27298 * @class Roo.bootstrap.menu.Item
27299 * @extends Roo.bootstrap.Component
27300 * Bootstrap MenuItem class
27301 * @cfg {Boolean} submenu (true | false) default false
27302 * @cfg {String} html text of the item
27303 * @cfg {String} href the link
27304 * @cfg {Boolean} disable (true | false) default false
27305 * @cfg {Boolean} preventDefault (true | false) default true
27306 * @cfg {String} icon Font awesome icon
27307 * @cfg {String} pos Submenu align to (left | right) default right
27311 * Create a new Item
27312 * @param {Object} config The config object
27316 Roo.bootstrap.menu.Item = function(config){
27317 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27321 * Fires when the mouse is hovering over this menu
27322 * @param {Roo.bootstrap.menu.Item} this
27323 * @param {Roo.EventObject} e
27328 * Fires when the mouse exits this menu
27329 * @param {Roo.bootstrap.menu.Item} this
27330 * @param {Roo.EventObject} e
27336 * The raw click event for the entire grid.
27337 * @param {Roo.EventObject} e
27343 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27348 preventDefault: true,
27353 getAutoCreate : function()
27358 cls : 'roo-menu-item-text',
27366 cls : 'fa ' + this.icon
27375 href : this.href || '#',
27382 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27386 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27388 if(this.pos == 'left'){
27389 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27396 initEvents : function()
27398 this.el.on('mouseover', this.onMouseOver, this);
27399 this.el.on('mouseout', this.onMouseOut, this);
27401 this.el.select('a', true).first().on('click', this.onClick, this);
27405 onClick : function(e)
27407 if(this.preventDefault){
27408 e.preventDefault();
27411 this.fireEvent("click", this, e);
27414 onMouseOver : function(e)
27416 if(this.submenu && this.pos == 'left'){
27417 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27420 this.fireEvent("mouseover", this, e);
27423 onMouseOut : function(e)
27425 this.fireEvent("mouseout", this, e);
27437 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27440 * @class Roo.bootstrap.menu.Separator
27441 * @extends Roo.bootstrap.Component
27442 * Bootstrap Separator class
27445 * Create a new Separator
27446 * @param {Object} config The config object
27450 Roo.bootstrap.menu.Separator = function(config){
27451 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27454 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
27456 getAutoCreate : function(){
27477 * @class Roo.bootstrap.Tooltip
27478 * Bootstrap Tooltip class
27479 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27480 * to determine which dom element triggers the tooltip.
27482 * It needs to add support for additional attributes like tooltip-position
27485 * Create a new Toolti
27486 * @param {Object} config The config object
27489 Roo.bootstrap.Tooltip = function(config){
27490 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27492 this.alignment = Roo.bootstrap.Tooltip.alignment;
27494 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27495 this.alignment = config.alignment;
27500 Roo.apply(Roo.bootstrap.Tooltip, {
27502 * @function init initialize tooltip monitoring.
27506 currentTip : false,
27507 currentRegion : false,
27513 Roo.get(document).on('mouseover', this.enter ,this);
27514 Roo.get(document).on('mouseout', this.leave, this);
27517 this.currentTip = new Roo.bootstrap.Tooltip();
27520 enter : function(ev)
27522 var dom = ev.getTarget();
27524 //Roo.log(['enter',dom]);
27525 var el = Roo.fly(dom);
27526 if (this.currentEl) {
27528 //Roo.log(this.currentEl);
27529 //Roo.log(this.currentEl.contains(dom));
27530 if (this.currentEl == el) {
27533 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27539 if (this.currentTip.el) {
27540 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27544 if(!el || el.dom == document){
27550 // you can not look for children, as if el is the body.. then everythign is the child..
27551 if (!el.attr('tooltip')) { //
27552 if (!el.select("[tooltip]").elements.length) {
27555 // is the mouse over this child...?
27556 bindEl = el.select("[tooltip]").first();
27557 var xy = ev.getXY();
27558 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27559 //Roo.log("not in region.");
27562 //Roo.log("child element over..");
27565 this.currentEl = bindEl;
27566 this.currentTip.bind(bindEl);
27567 this.currentRegion = Roo.lib.Region.getRegion(dom);
27568 this.currentTip.enter();
27571 leave : function(ev)
27573 var dom = ev.getTarget();
27574 //Roo.log(['leave',dom]);
27575 if (!this.currentEl) {
27580 if (dom != this.currentEl.dom) {
27583 var xy = ev.getXY();
27584 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
27587 // only activate leave if mouse cursor is outside... bounding box..
27592 if (this.currentTip) {
27593 this.currentTip.leave();
27595 //Roo.log('clear currentEl');
27596 this.currentEl = false;
27601 'left' : ['r-l', [-2,0], 'right'],
27602 'right' : ['l-r', [2,0], 'left'],
27603 'bottom' : ['t-b', [0,2], 'top'],
27604 'top' : [ 'b-t', [0,-2], 'bottom']
27610 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27615 delay : null, // can be { show : 300 , hide: 500}
27619 hoverState : null, //???
27621 placement : 'bottom',
27625 getAutoCreate : function(){
27632 cls : 'tooltip-arrow'
27635 cls : 'tooltip-inner'
27642 bind : function(el)
27648 enter : function () {
27650 if (this.timeout != null) {
27651 clearTimeout(this.timeout);
27654 this.hoverState = 'in';
27655 //Roo.log("enter - show");
27656 if (!this.delay || !this.delay.show) {
27661 this.timeout = setTimeout(function () {
27662 if (_t.hoverState == 'in') {
27665 }, this.delay.show);
27669 clearTimeout(this.timeout);
27671 this.hoverState = 'out';
27672 if (!this.delay || !this.delay.hide) {
27678 this.timeout = setTimeout(function () {
27679 //Roo.log("leave - timeout");
27681 if (_t.hoverState == 'out') {
27683 Roo.bootstrap.Tooltip.currentEl = false;
27688 show : function (msg)
27691 this.render(document.body);
27694 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27696 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27698 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27700 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27702 var placement = typeof this.placement == 'function' ?
27703 this.placement.call(this, this.el, on_el) :
27706 var autoToken = /\s?auto?\s?/i;
27707 var autoPlace = autoToken.test(placement);
27709 placement = placement.replace(autoToken, '') || 'top';
27713 //this.el.setXY([0,0]);
27715 //this.el.dom.style.display='block';
27717 //this.el.appendTo(on_el);
27719 var p = this.getPosition();
27720 var box = this.el.getBox();
27726 var align = this.alignment[placement];
27728 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27730 if(placement == 'top' || placement == 'bottom'){
27732 placement = 'right';
27735 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27736 placement = 'left';
27739 var scroll = Roo.select('body', true).first().getScroll();
27741 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27745 align = this.alignment[placement];
27748 this.el.alignTo(this.bindEl, align[0],align[1]);
27749 //var arrow = this.el.select('.arrow',true).first();
27750 //arrow.set(align[2],
27752 this.el.addClass(placement);
27754 this.el.addClass('in fade');
27756 this.hoverState = null;
27758 if (this.el.hasClass('fade')) {
27769 //this.el.setXY([0,0]);
27770 this.el.removeClass('in');
27786 * @class Roo.bootstrap.LocationPicker
27787 * @extends Roo.bootstrap.Component
27788 * Bootstrap LocationPicker class
27789 * @cfg {Number} latitude Position when init default 0
27790 * @cfg {Number} longitude Position when init default 0
27791 * @cfg {Number} zoom default 15
27792 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27793 * @cfg {Boolean} mapTypeControl default false
27794 * @cfg {Boolean} disableDoubleClickZoom default false
27795 * @cfg {Boolean} scrollwheel default true
27796 * @cfg {Boolean} streetViewControl default false
27797 * @cfg {Number} radius default 0
27798 * @cfg {String} locationName
27799 * @cfg {Boolean} draggable default true
27800 * @cfg {Boolean} enableAutocomplete default false
27801 * @cfg {Boolean} enableReverseGeocode default true
27802 * @cfg {String} markerTitle
27805 * Create a new LocationPicker
27806 * @param {Object} config The config object
27810 Roo.bootstrap.LocationPicker = function(config){
27812 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27817 * Fires when the picker initialized.
27818 * @param {Roo.bootstrap.LocationPicker} this
27819 * @param {Google Location} location
27823 * @event positionchanged
27824 * Fires when the picker position changed.
27825 * @param {Roo.bootstrap.LocationPicker} this
27826 * @param {Google Location} location
27828 positionchanged : true,
27831 * Fires when the map resize.
27832 * @param {Roo.bootstrap.LocationPicker} this
27837 * Fires when the map show.
27838 * @param {Roo.bootstrap.LocationPicker} this
27843 * Fires when the map hide.
27844 * @param {Roo.bootstrap.LocationPicker} this
27849 * Fires when click the map.
27850 * @param {Roo.bootstrap.LocationPicker} this
27851 * @param {Map event} e
27855 * @event mapRightClick
27856 * Fires when right click the map.
27857 * @param {Roo.bootstrap.LocationPicker} this
27858 * @param {Map event} e
27860 mapRightClick : true,
27862 * @event markerClick
27863 * Fires when click the marker.
27864 * @param {Roo.bootstrap.LocationPicker} this
27865 * @param {Map event} e
27867 markerClick : true,
27869 * @event markerRightClick
27870 * Fires when right click the marker.
27871 * @param {Roo.bootstrap.LocationPicker} this
27872 * @param {Map event} e
27874 markerRightClick : true,
27876 * @event OverlayViewDraw
27877 * Fires when OverlayView Draw
27878 * @param {Roo.bootstrap.LocationPicker} this
27880 OverlayViewDraw : true,
27882 * @event OverlayViewOnAdd
27883 * Fires when OverlayView Draw
27884 * @param {Roo.bootstrap.LocationPicker} this
27886 OverlayViewOnAdd : true,
27888 * @event OverlayViewOnRemove
27889 * Fires when OverlayView Draw
27890 * @param {Roo.bootstrap.LocationPicker} this
27892 OverlayViewOnRemove : true,
27894 * @event OverlayViewShow
27895 * Fires when OverlayView Draw
27896 * @param {Roo.bootstrap.LocationPicker} this
27897 * @param {Pixel} cpx
27899 OverlayViewShow : true,
27901 * @event OverlayViewHide
27902 * Fires when OverlayView Draw
27903 * @param {Roo.bootstrap.LocationPicker} this
27905 OverlayViewHide : true,
27907 * @event loadexception
27908 * Fires when load google lib failed.
27909 * @param {Roo.bootstrap.LocationPicker} this
27911 loadexception : true
27916 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27918 gMapContext: false,
27924 mapTypeControl: false,
27925 disableDoubleClickZoom: false,
27927 streetViewControl: false,
27931 enableAutocomplete: false,
27932 enableReverseGeocode: true,
27935 getAutoCreate: function()
27940 cls: 'roo-location-picker'
27946 initEvents: function(ct, position)
27948 if(!this.el.getWidth() || this.isApplied()){
27952 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27957 initial: function()
27959 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27960 this.fireEvent('loadexception', this);
27964 if(!this.mapTypeId){
27965 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27968 this.gMapContext = this.GMapContext();
27970 this.initOverlayView();
27972 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27976 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27977 _this.setPosition(_this.gMapContext.marker.position);
27980 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27981 _this.fireEvent('mapClick', this, event);
27985 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27986 _this.fireEvent('mapRightClick', this, event);
27990 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27991 _this.fireEvent('markerClick', this, event);
27995 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27996 _this.fireEvent('markerRightClick', this, event);
28000 this.setPosition(this.gMapContext.location);
28002 this.fireEvent('initial', this, this.gMapContext.location);
28005 initOverlayView: function()
28009 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28013 _this.fireEvent('OverlayViewDraw', _this);
28018 _this.fireEvent('OverlayViewOnAdd', _this);
28021 onRemove: function()
28023 _this.fireEvent('OverlayViewOnRemove', _this);
28026 show: function(cpx)
28028 _this.fireEvent('OverlayViewShow', _this, cpx);
28033 _this.fireEvent('OverlayViewHide', _this);
28039 fromLatLngToContainerPixel: function(event)
28041 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28044 isApplied: function()
28046 return this.getGmapContext() == false ? false : true;
28049 getGmapContext: function()
28051 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28054 GMapContext: function()
28056 var position = new google.maps.LatLng(this.latitude, this.longitude);
28058 var _map = new google.maps.Map(this.el.dom, {
28061 mapTypeId: this.mapTypeId,
28062 mapTypeControl: this.mapTypeControl,
28063 disableDoubleClickZoom: this.disableDoubleClickZoom,
28064 scrollwheel: this.scrollwheel,
28065 streetViewControl: this.streetViewControl,
28066 locationName: this.locationName,
28067 draggable: this.draggable,
28068 enableAutocomplete: this.enableAutocomplete,
28069 enableReverseGeocode: this.enableReverseGeocode
28072 var _marker = new google.maps.Marker({
28073 position: position,
28075 title: this.markerTitle,
28076 draggable: this.draggable
28083 location: position,
28084 radius: this.radius,
28085 locationName: this.locationName,
28086 addressComponents: {
28087 formatted_address: null,
28088 addressLine1: null,
28089 addressLine2: null,
28091 streetNumber: null,
28095 stateOrProvince: null
28098 domContainer: this.el.dom,
28099 geodecoder: new google.maps.Geocoder()
28103 drawCircle: function(center, radius, options)
28105 if (this.gMapContext.circle != null) {
28106 this.gMapContext.circle.setMap(null);
28110 options = Roo.apply({}, options, {
28111 strokeColor: "#0000FF",
28112 strokeOpacity: .35,
28114 fillColor: "#0000FF",
28118 options.map = this.gMapContext.map;
28119 options.radius = radius;
28120 options.center = center;
28121 this.gMapContext.circle = new google.maps.Circle(options);
28122 return this.gMapContext.circle;
28128 setPosition: function(location)
28130 this.gMapContext.location = location;
28131 this.gMapContext.marker.setPosition(location);
28132 this.gMapContext.map.panTo(location);
28133 this.drawCircle(location, this.gMapContext.radius, {});
28137 if (this.gMapContext.settings.enableReverseGeocode) {
28138 this.gMapContext.geodecoder.geocode({
28139 latLng: this.gMapContext.location
28140 }, function(results, status) {
28142 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28143 _this.gMapContext.locationName = results[0].formatted_address;
28144 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28146 _this.fireEvent('positionchanged', this, location);
28153 this.fireEvent('positionchanged', this, location);
28158 google.maps.event.trigger(this.gMapContext.map, "resize");
28160 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28162 this.fireEvent('resize', this);
28165 setPositionByLatLng: function(latitude, longitude)
28167 this.setPosition(new google.maps.LatLng(latitude, longitude));
28170 getCurrentPosition: function()
28173 latitude: this.gMapContext.location.lat(),
28174 longitude: this.gMapContext.location.lng()
28178 getAddressName: function()
28180 return this.gMapContext.locationName;
28183 getAddressComponents: function()
28185 return this.gMapContext.addressComponents;
28188 address_component_from_google_geocode: function(address_components)
28192 for (var i = 0; i < address_components.length; i++) {
28193 var component = address_components[i];
28194 if (component.types.indexOf("postal_code") >= 0) {
28195 result.postalCode = component.short_name;
28196 } else if (component.types.indexOf("street_number") >= 0) {
28197 result.streetNumber = component.short_name;
28198 } else if (component.types.indexOf("route") >= 0) {
28199 result.streetName = component.short_name;
28200 } else if (component.types.indexOf("neighborhood") >= 0) {
28201 result.city = component.short_name;
28202 } else if (component.types.indexOf("locality") >= 0) {
28203 result.city = component.short_name;
28204 } else if (component.types.indexOf("sublocality") >= 0) {
28205 result.district = component.short_name;
28206 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28207 result.stateOrProvince = component.short_name;
28208 } else if (component.types.indexOf("country") >= 0) {
28209 result.country = component.short_name;
28213 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28214 result.addressLine2 = "";
28218 setZoomLevel: function(zoom)
28220 this.gMapContext.map.setZoom(zoom);
28233 this.fireEvent('show', this);
28244 this.fireEvent('hide', this);
28249 Roo.apply(Roo.bootstrap.LocationPicker, {
28251 OverlayView : function(map, options)
28253 options = options || {};
28260 * @class Roo.bootstrap.Alert
28261 * @extends Roo.bootstrap.Component
28262 * Bootstrap Alert class - shows an alert area box
28264 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28265 Enter a valid email address
28268 * @cfg {String} title The title of alert
28269 * @cfg {String} html The content of alert
28270 * @cfg {String} weight ( success | info | warning | danger )
28271 * @cfg {String} faicon font-awesomeicon
28274 * Create a new alert
28275 * @param {Object} config The config object
28279 Roo.bootstrap.Alert = function(config){
28280 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28284 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28291 getAutoCreate : function()
28300 cls : 'roo-alert-icon'
28305 cls : 'roo-alert-title',
28310 cls : 'roo-alert-text',
28317 cfg.cn[0].cls += ' fa ' + this.faicon;
28321 cfg.cls += ' alert-' + this.weight;
28327 initEvents: function()
28329 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28332 setTitle : function(str)
28334 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28337 setText : function(str)
28339 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28342 setWeight : function(weight)
28345 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28348 this.weight = weight;
28350 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28353 setIcon : function(icon)
28356 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28359 this.faicon = icon;
28361 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28382 * @class Roo.bootstrap.UploadCropbox
28383 * @extends Roo.bootstrap.Component
28384 * Bootstrap UploadCropbox class
28385 * @cfg {String} emptyText show when image has been loaded
28386 * @cfg {String} rotateNotify show when image too small to rotate
28387 * @cfg {Number} errorTimeout default 3000
28388 * @cfg {Number} minWidth default 300
28389 * @cfg {Number} minHeight default 300
28390 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28391 * @cfg {Boolean} isDocument (true|false) default false
28392 * @cfg {String} url action url
28393 * @cfg {String} paramName default 'imageUpload'
28394 * @cfg {String} method default POST
28395 * @cfg {Boolean} loadMask (true|false) default true
28396 * @cfg {Boolean} loadingText default 'Loading...'
28399 * Create a new UploadCropbox
28400 * @param {Object} config The config object
28403 Roo.bootstrap.UploadCropbox = function(config){
28404 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28408 * @event beforeselectfile
28409 * Fire before select file
28410 * @param {Roo.bootstrap.UploadCropbox} this
28412 "beforeselectfile" : true,
28415 * Fire after initEvent
28416 * @param {Roo.bootstrap.UploadCropbox} this
28421 * Fire after initEvent
28422 * @param {Roo.bootstrap.UploadCropbox} this
28423 * @param {String} data
28428 * Fire when preparing the file data
28429 * @param {Roo.bootstrap.UploadCropbox} this
28430 * @param {Object} file
28435 * Fire when get exception
28436 * @param {Roo.bootstrap.UploadCropbox} this
28437 * @param {XMLHttpRequest} xhr
28439 "exception" : true,
28441 * @event beforeloadcanvas
28442 * Fire before load the canvas
28443 * @param {Roo.bootstrap.UploadCropbox} this
28444 * @param {String} src
28446 "beforeloadcanvas" : true,
28449 * Fire when trash image
28450 * @param {Roo.bootstrap.UploadCropbox} this
28455 * Fire when download the image
28456 * @param {Roo.bootstrap.UploadCropbox} this
28460 * @event footerbuttonclick
28461 * Fire when footerbuttonclick
28462 * @param {Roo.bootstrap.UploadCropbox} this
28463 * @param {String} type
28465 "footerbuttonclick" : true,
28469 * @param {Roo.bootstrap.UploadCropbox} this
28474 * Fire when rotate the image
28475 * @param {Roo.bootstrap.UploadCropbox} this
28476 * @param {String} pos
28481 * Fire when inspect the file
28482 * @param {Roo.bootstrap.UploadCropbox} this
28483 * @param {Object} file
28488 * Fire when xhr upload the file
28489 * @param {Roo.bootstrap.UploadCropbox} this
28490 * @param {Object} data
28495 * Fire when arrange the file data
28496 * @param {Roo.bootstrap.UploadCropbox} this
28497 * @param {Object} formData
28502 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28505 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
28507 emptyText : 'Click to upload image',
28508 rotateNotify : 'Image is too small to rotate',
28509 errorTimeout : 3000,
28523 cropType : 'image/jpeg',
28525 canvasLoaded : false,
28526 isDocument : false,
28528 paramName : 'imageUpload',
28530 loadingText : 'Loading...',
28533 getAutoCreate : function()
28537 cls : 'roo-upload-cropbox',
28541 cls : 'roo-upload-cropbox-selector',
28546 cls : 'roo-upload-cropbox-body',
28547 style : 'cursor:pointer',
28551 cls : 'roo-upload-cropbox-preview'
28555 cls : 'roo-upload-cropbox-thumb'
28559 cls : 'roo-upload-cropbox-empty-notify',
28560 html : this.emptyText
28564 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28565 html : this.rotateNotify
28571 cls : 'roo-upload-cropbox-footer',
28574 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28584 onRender : function(ct, position)
28586 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28588 if (this.buttons.length) {
28590 Roo.each(this.buttons, function(bb) {
28592 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28594 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28600 this.maskEl = this.el;
28604 initEvents : function()
28606 this.urlAPI = (window.createObjectURL && window) ||
28607 (window.URL && URL.revokeObjectURL && URL) ||
28608 (window.webkitURL && webkitURL);
28610 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28611 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28613 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28614 this.selectorEl.hide();
28616 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28617 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28619 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28620 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28621 this.thumbEl.hide();
28623 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28624 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28626 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28627 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28628 this.errorEl.hide();
28630 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28631 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28632 this.footerEl.hide();
28634 this.setThumbBoxSize();
28640 this.fireEvent('initial', this);
28647 window.addEventListener("resize", function() { _this.resize(); } );
28649 this.bodyEl.on('click', this.beforeSelectFile, this);
28652 this.bodyEl.on('touchstart', this.onTouchStart, this);
28653 this.bodyEl.on('touchmove', this.onTouchMove, this);
28654 this.bodyEl.on('touchend', this.onTouchEnd, this);
28658 this.bodyEl.on('mousedown', this.onMouseDown, this);
28659 this.bodyEl.on('mousemove', this.onMouseMove, this);
28660 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28661 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28662 Roo.get(document).on('mouseup', this.onMouseUp, this);
28665 this.selectorEl.on('change', this.onFileSelected, this);
28671 this.baseScale = 1;
28673 this.baseRotate = 1;
28674 this.dragable = false;
28675 this.pinching = false;
28678 this.cropData = false;
28679 this.notifyEl.dom.innerHTML = this.emptyText;
28681 this.selectorEl.dom.value = '';
28685 resize : function()
28687 if(this.fireEvent('resize', this) != false){
28688 this.setThumbBoxPosition();
28689 this.setCanvasPosition();
28693 onFooterButtonClick : function(e, el, o, type)
28696 case 'rotate-left' :
28697 this.onRotateLeft(e);
28699 case 'rotate-right' :
28700 this.onRotateRight(e);
28703 this.beforeSelectFile(e);
28718 this.fireEvent('footerbuttonclick', this, type);
28721 beforeSelectFile : function(e)
28723 e.preventDefault();
28725 if(this.fireEvent('beforeselectfile', this) != false){
28726 this.selectorEl.dom.click();
28730 onFileSelected : function(e)
28732 e.preventDefault();
28734 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28738 var file = this.selectorEl.dom.files[0];
28740 if(this.fireEvent('inspect', this, file) != false){
28741 this.prepare(file);
28746 trash : function(e)
28748 this.fireEvent('trash', this);
28751 download : function(e)
28753 this.fireEvent('download', this);
28756 loadCanvas : function(src)
28758 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28762 this.imageEl = document.createElement('img');
28766 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28768 this.imageEl.src = src;
28772 onLoadCanvas : function()
28774 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28775 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28777 this.bodyEl.un('click', this.beforeSelectFile, this);
28779 this.notifyEl.hide();
28780 this.thumbEl.show();
28781 this.footerEl.show();
28783 this.baseRotateLevel();
28785 if(this.isDocument){
28786 this.setThumbBoxSize();
28789 this.setThumbBoxPosition();
28791 this.baseScaleLevel();
28797 this.canvasLoaded = true;
28800 this.maskEl.unmask();
28805 setCanvasPosition : function()
28807 if(!this.canvasEl){
28811 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28812 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28814 this.previewEl.setLeft(pw);
28815 this.previewEl.setTop(ph);
28819 onMouseDown : function(e)
28823 this.dragable = true;
28824 this.pinching = false;
28826 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28827 this.dragable = false;
28831 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28832 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28836 onMouseMove : function(e)
28840 if(!this.canvasLoaded){
28844 if (!this.dragable){
28848 var minX = Math.ceil(this.thumbEl.getLeft(true));
28849 var minY = Math.ceil(this.thumbEl.getTop(true));
28851 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28852 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28854 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28855 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28857 x = x - this.mouseX;
28858 y = y - this.mouseY;
28860 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28861 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28863 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28864 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28866 this.previewEl.setLeft(bgX);
28867 this.previewEl.setTop(bgY);
28869 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28870 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28873 onMouseUp : function(e)
28877 this.dragable = false;
28880 onMouseWheel : function(e)
28884 this.startScale = this.scale;
28886 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28888 if(!this.zoomable()){
28889 this.scale = this.startScale;
28898 zoomable : function()
28900 var minScale = this.thumbEl.getWidth() / this.minWidth;
28902 if(this.minWidth < this.minHeight){
28903 minScale = this.thumbEl.getHeight() / this.minHeight;
28906 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28907 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28911 (this.rotate == 0 || this.rotate == 180) &&
28913 width > this.imageEl.OriginWidth ||
28914 height > this.imageEl.OriginHeight ||
28915 (width < this.minWidth && height < this.minHeight)
28923 (this.rotate == 90 || this.rotate == 270) &&
28925 width > this.imageEl.OriginWidth ||
28926 height > this.imageEl.OriginHeight ||
28927 (width < this.minHeight && height < this.minWidth)
28934 !this.isDocument &&
28935 (this.rotate == 0 || this.rotate == 180) &&
28937 width < this.minWidth ||
28938 width > this.imageEl.OriginWidth ||
28939 height < this.minHeight ||
28940 height > this.imageEl.OriginHeight
28947 !this.isDocument &&
28948 (this.rotate == 90 || this.rotate == 270) &&
28950 width < this.minHeight ||
28951 width > this.imageEl.OriginWidth ||
28952 height < this.minWidth ||
28953 height > this.imageEl.OriginHeight
28963 onRotateLeft : function(e)
28965 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28967 var minScale = this.thumbEl.getWidth() / this.minWidth;
28969 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28970 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28972 this.startScale = this.scale;
28974 while (this.getScaleLevel() < minScale){
28976 this.scale = this.scale + 1;
28978 if(!this.zoomable()){
28983 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28984 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28989 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28996 this.scale = this.startScale;
28998 this.onRotateFail();
29003 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29005 if(this.isDocument){
29006 this.setThumbBoxSize();
29007 this.setThumbBoxPosition();
29008 this.setCanvasPosition();
29013 this.fireEvent('rotate', this, 'left');
29017 onRotateRight : function(e)
29019 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29021 var minScale = this.thumbEl.getWidth() / this.minWidth;
29023 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29024 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29026 this.startScale = this.scale;
29028 while (this.getScaleLevel() < minScale){
29030 this.scale = this.scale + 1;
29032 if(!this.zoomable()){
29037 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29038 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29043 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29050 this.scale = this.startScale;
29052 this.onRotateFail();
29057 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29059 if(this.isDocument){
29060 this.setThumbBoxSize();
29061 this.setThumbBoxPosition();
29062 this.setCanvasPosition();
29067 this.fireEvent('rotate', this, 'right');
29070 onRotateFail : function()
29072 this.errorEl.show(true);
29076 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29081 this.previewEl.dom.innerHTML = '';
29083 var canvasEl = document.createElement("canvas");
29085 var contextEl = canvasEl.getContext("2d");
29087 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29088 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29089 var center = this.imageEl.OriginWidth / 2;
29091 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29092 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29093 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29094 center = this.imageEl.OriginHeight / 2;
29097 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29099 contextEl.translate(center, center);
29100 contextEl.rotate(this.rotate * Math.PI / 180);
29102 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29104 this.canvasEl = document.createElement("canvas");
29106 this.contextEl = this.canvasEl.getContext("2d");
29108 switch (this.rotate) {
29111 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29112 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29114 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29119 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29120 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29122 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29123 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);
29127 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29132 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29133 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29135 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29136 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);
29140 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);
29145 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29146 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29148 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29149 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29153 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);
29160 this.previewEl.appendChild(this.canvasEl);
29162 this.setCanvasPosition();
29167 if(!this.canvasLoaded){
29171 var imageCanvas = document.createElement("canvas");
29173 var imageContext = imageCanvas.getContext("2d");
29175 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29176 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29178 var center = imageCanvas.width / 2;
29180 imageContext.translate(center, center);
29182 imageContext.rotate(this.rotate * Math.PI / 180);
29184 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29186 var canvas = document.createElement("canvas");
29188 var context = canvas.getContext("2d");
29190 canvas.width = this.minWidth;
29191 canvas.height = this.minHeight;
29193 switch (this.rotate) {
29196 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29197 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29199 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29200 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29202 var targetWidth = this.minWidth - 2 * x;
29203 var targetHeight = this.minHeight - 2 * y;
29207 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29208 scale = targetWidth / width;
29211 if(x > 0 && y == 0){
29212 scale = targetHeight / height;
29215 if(x > 0 && y > 0){
29216 scale = targetWidth / width;
29218 if(width < height){
29219 scale = targetHeight / height;
29223 context.scale(scale, scale);
29225 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29226 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29228 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29229 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29231 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29236 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29237 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29239 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29240 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29242 var targetWidth = this.minWidth - 2 * x;
29243 var targetHeight = this.minHeight - 2 * y;
29247 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29248 scale = targetWidth / width;
29251 if(x > 0 && y == 0){
29252 scale = targetHeight / height;
29255 if(x > 0 && y > 0){
29256 scale = targetWidth / width;
29258 if(width < height){
29259 scale = targetHeight / height;
29263 context.scale(scale, scale);
29265 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29266 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29268 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29269 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29271 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29273 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29278 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29279 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29281 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29282 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29284 var targetWidth = this.minWidth - 2 * x;
29285 var targetHeight = this.minHeight - 2 * y;
29289 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29290 scale = targetWidth / width;
29293 if(x > 0 && y == 0){
29294 scale = targetHeight / height;
29297 if(x > 0 && y > 0){
29298 scale = targetWidth / width;
29300 if(width < height){
29301 scale = targetHeight / height;
29305 context.scale(scale, scale);
29307 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29308 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29310 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29311 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29313 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29314 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29316 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29321 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29322 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29324 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29325 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29327 var targetWidth = this.minWidth - 2 * x;
29328 var targetHeight = this.minHeight - 2 * y;
29332 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29333 scale = targetWidth / width;
29336 if(x > 0 && y == 0){
29337 scale = targetHeight / height;
29340 if(x > 0 && y > 0){
29341 scale = targetWidth / width;
29343 if(width < height){
29344 scale = targetHeight / height;
29348 context.scale(scale, scale);
29350 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29351 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29353 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29354 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29356 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29358 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29365 this.cropData = canvas.toDataURL(this.cropType);
29367 if(this.fireEvent('crop', this, this.cropData) !== false){
29368 this.process(this.file, this.cropData);
29375 setThumbBoxSize : function()
29379 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29380 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29381 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29383 this.minWidth = width;
29384 this.minHeight = height;
29386 if(this.rotate == 90 || this.rotate == 270){
29387 this.minWidth = height;
29388 this.minHeight = width;
29393 width = Math.ceil(this.minWidth * height / this.minHeight);
29395 if(this.minWidth > this.minHeight){
29397 height = Math.ceil(this.minHeight * width / this.minWidth);
29400 this.thumbEl.setStyle({
29401 width : width + 'px',
29402 height : height + 'px'
29409 setThumbBoxPosition : function()
29411 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29412 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29414 this.thumbEl.setLeft(x);
29415 this.thumbEl.setTop(y);
29419 baseRotateLevel : function()
29421 this.baseRotate = 1;
29424 typeof(this.exif) != 'undefined' &&
29425 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29426 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29428 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29431 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29435 baseScaleLevel : function()
29439 if(this.isDocument){
29441 if(this.baseRotate == 6 || this.baseRotate == 8){
29443 height = this.thumbEl.getHeight();
29444 this.baseScale = height / this.imageEl.OriginWidth;
29446 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29447 width = this.thumbEl.getWidth();
29448 this.baseScale = width / this.imageEl.OriginHeight;
29454 height = this.thumbEl.getHeight();
29455 this.baseScale = height / this.imageEl.OriginHeight;
29457 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29458 width = this.thumbEl.getWidth();
29459 this.baseScale = width / this.imageEl.OriginWidth;
29465 if(this.baseRotate == 6 || this.baseRotate == 8){
29467 width = this.thumbEl.getHeight();
29468 this.baseScale = width / this.imageEl.OriginHeight;
29470 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29471 height = this.thumbEl.getWidth();
29472 this.baseScale = height / this.imageEl.OriginHeight;
29475 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29476 height = this.thumbEl.getWidth();
29477 this.baseScale = height / this.imageEl.OriginHeight;
29479 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29480 width = this.thumbEl.getHeight();
29481 this.baseScale = width / this.imageEl.OriginWidth;
29488 width = this.thumbEl.getWidth();
29489 this.baseScale = width / this.imageEl.OriginWidth;
29491 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29492 height = this.thumbEl.getHeight();
29493 this.baseScale = height / this.imageEl.OriginHeight;
29496 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29498 height = this.thumbEl.getHeight();
29499 this.baseScale = height / this.imageEl.OriginHeight;
29501 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29502 width = this.thumbEl.getWidth();
29503 this.baseScale = width / this.imageEl.OriginWidth;
29511 getScaleLevel : function()
29513 return this.baseScale * Math.pow(1.1, this.scale);
29516 onTouchStart : function(e)
29518 if(!this.canvasLoaded){
29519 this.beforeSelectFile(e);
29523 var touches = e.browserEvent.touches;
29529 if(touches.length == 1){
29530 this.onMouseDown(e);
29534 if(touches.length != 2){
29540 for(var i = 0, finger; finger = touches[i]; i++){
29541 coords.push(finger.pageX, finger.pageY);
29544 var x = Math.pow(coords[0] - coords[2], 2);
29545 var y = Math.pow(coords[1] - coords[3], 2);
29547 this.startDistance = Math.sqrt(x + y);
29549 this.startScale = this.scale;
29551 this.pinching = true;
29552 this.dragable = false;
29556 onTouchMove : function(e)
29558 if(!this.pinching && !this.dragable){
29562 var touches = e.browserEvent.touches;
29569 this.onMouseMove(e);
29575 for(var i = 0, finger; finger = touches[i]; i++){
29576 coords.push(finger.pageX, finger.pageY);
29579 var x = Math.pow(coords[0] - coords[2], 2);
29580 var y = Math.pow(coords[1] - coords[3], 2);
29582 this.endDistance = Math.sqrt(x + y);
29584 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29586 if(!this.zoomable()){
29587 this.scale = this.startScale;
29595 onTouchEnd : function(e)
29597 this.pinching = false;
29598 this.dragable = false;
29602 process : function(file, crop)
29605 this.maskEl.mask(this.loadingText);
29608 this.xhr = new XMLHttpRequest();
29610 file.xhr = this.xhr;
29612 this.xhr.open(this.method, this.url, true);
29615 "Accept": "application/json",
29616 "Cache-Control": "no-cache",
29617 "X-Requested-With": "XMLHttpRequest"
29620 for (var headerName in headers) {
29621 var headerValue = headers[headerName];
29623 this.xhr.setRequestHeader(headerName, headerValue);
29629 this.xhr.onload = function()
29631 _this.xhrOnLoad(_this.xhr);
29634 this.xhr.onerror = function()
29636 _this.xhrOnError(_this.xhr);
29639 var formData = new FormData();
29641 formData.append('returnHTML', 'NO');
29644 formData.append('crop', crop);
29647 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29648 formData.append(this.paramName, file, file.name);
29651 if(typeof(file.filename) != 'undefined'){
29652 formData.append('filename', file.filename);
29655 if(typeof(file.mimetype) != 'undefined'){
29656 formData.append('mimetype', file.mimetype);
29659 if(this.fireEvent('arrange', this, formData) != false){
29660 this.xhr.send(formData);
29664 xhrOnLoad : function(xhr)
29667 this.maskEl.unmask();
29670 if (xhr.readyState !== 4) {
29671 this.fireEvent('exception', this, xhr);
29675 var response = Roo.decode(xhr.responseText);
29677 if(!response.success){
29678 this.fireEvent('exception', this, xhr);
29682 var response = Roo.decode(xhr.responseText);
29684 this.fireEvent('upload', this, response);
29688 xhrOnError : function()
29691 this.maskEl.unmask();
29694 Roo.log('xhr on error');
29696 var response = Roo.decode(xhr.responseText);
29702 prepare : function(file)
29705 this.maskEl.mask(this.loadingText);
29711 if(typeof(file) === 'string'){
29712 this.loadCanvas(file);
29716 if(!file || !this.urlAPI){
29721 this.cropType = file.type;
29725 if(this.fireEvent('prepare', this, this.file) != false){
29727 var reader = new FileReader();
29729 reader.onload = function (e) {
29730 if (e.target.error) {
29731 Roo.log(e.target.error);
29735 var buffer = e.target.result,
29736 dataView = new DataView(buffer),
29738 maxOffset = dataView.byteLength - 4,
29742 if (dataView.getUint16(0) === 0xffd8) {
29743 while (offset < maxOffset) {
29744 markerBytes = dataView.getUint16(offset);
29746 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29747 markerLength = dataView.getUint16(offset + 2) + 2;
29748 if (offset + markerLength > dataView.byteLength) {
29749 Roo.log('Invalid meta data: Invalid segment size.');
29753 if(markerBytes == 0xffe1){
29754 _this.parseExifData(
29761 offset += markerLength;
29771 var url = _this.urlAPI.createObjectURL(_this.file);
29773 _this.loadCanvas(url);
29778 reader.readAsArrayBuffer(this.file);
29784 parseExifData : function(dataView, offset, length)
29786 var tiffOffset = offset + 10,
29790 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29791 // No Exif data, might be XMP data instead
29795 // Check for the ASCII code for "Exif" (0x45786966):
29796 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29797 // No Exif data, might be XMP data instead
29800 if (tiffOffset + 8 > dataView.byteLength) {
29801 Roo.log('Invalid Exif data: Invalid segment size.');
29804 // Check for the two null bytes:
29805 if (dataView.getUint16(offset + 8) !== 0x0000) {
29806 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29809 // Check the byte alignment:
29810 switch (dataView.getUint16(tiffOffset)) {
29812 littleEndian = true;
29815 littleEndian = false;
29818 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29821 // Check for the TIFF tag marker (0x002A):
29822 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29823 Roo.log('Invalid Exif data: Missing TIFF marker.');
29826 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29827 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29829 this.parseExifTags(
29832 tiffOffset + dirOffset,
29837 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29842 if (dirOffset + 6 > dataView.byteLength) {
29843 Roo.log('Invalid Exif data: Invalid directory offset.');
29846 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29847 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29848 if (dirEndOffset + 4 > dataView.byteLength) {
29849 Roo.log('Invalid Exif data: Invalid directory size.');
29852 for (i = 0; i < tagsNumber; i += 1) {
29856 dirOffset + 2 + 12 * i, // tag offset
29860 // Return the offset to the next directory:
29861 return dataView.getUint32(dirEndOffset, littleEndian);
29864 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29866 var tag = dataView.getUint16(offset, littleEndian);
29868 this.exif[tag] = this.getExifValue(
29872 dataView.getUint16(offset + 2, littleEndian), // tag type
29873 dataView.getUint32(offset + 4, littleEndian), // tag length
29878 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29880 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29889 Roo.log('Invalid Exif data: Invalid tag type.');
29893 tagSize = tagType.size * length;
29894 // Determine if the value is contained in the dataOffset bytes,
29895 // or if the value at the dataOffset is a pointer to the actual data:
29896 dataOffset = tagSize > 4 ?
29897 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29898 if (dataOffset + tagSize > dataView.byteLength) {
29899 Roo.log('Invalid Exif data: Invalid data offset.');
29902 if (length === 1) {
29903 return tagType.getValue(dataView, dataOffset, littleEndian);
29906 for (i = 0; i < length; i += 1) {
29907 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29910 if (tagType.ascii) {
29912 // Concatenate the chars:
29913 for (i = 0; i < values.length; i += 1) {
29915 // Ignore the terminating NULL byte(s):
29916 if (c === '\u0000') {
29928 Roo.apply(Roo.bootstrap.UploadCropbox, {
29930 'Orientation': 0x0112
29934 1: 0, //'top-left',
29936 3: 180, //'bottom-right',
29937 // 4: 'bottom-left',
29939 6: 90, //'right-top',
29940 // 7: 'right-bottom',
29941 8: 270 //'left-bottom'
29945 // byte, 8-bit unsigned int:
29947 getValue: function (dataView, dataOffset) {
29948 return dataView.getUint8(dataOffset);
29952 // ascii, 8-bit byte:
29954 getValue: function (dataView, dataOffset) {
29955 return String.fromCharCode(dataView.getUint8(dataOffset));
29960 // short, 16 bit int:
29962 getValue: function (dataView, dataOffset, littleEndian) {
29963 return dataView.getUint16(dataOffset, littleEndian);
29967 // long, 32 bit int:
29969 getValue: function (dataView, dataOffset, littleEndian) {
29970 return dataView.getUint32(dataOffset, littleEndian);
29974 // rational = two long values, first is numerator, second is denominator:
29976 getValue: function (dataView, dataOffset, littleEndian) {
29977 return dataView.getUint32(dataOffset, littleEndian) /
29978 dataView.getUint32(dataOffset + 4, littleEndian);
29982 // slong, 32 bit signed int:
29984 getValue: function (dataView, dataOffset, littleEndian) {
29985 return dataView.getInt32(dataOffset, littleEndian);
29989 // srational, two slongs, first is numerator, second is denominator:
29991 getValue: function (dataView, dataOffset, littleEndian) {
29992 return dataView.getInt32(dataOffset, littleEndian) /
29993 dataView.getInt32(dataOffset + 4, littleEndian);
30003 cls : 'btn-group roo-upload-cropbox-rotate-left',
30004 action : 'rotate-left',
30008 cls : 'btn btn-default',
30009 html : '<i class="fa fa-undo"></i>'
30015 cls : 'btn-group roo-upload-cropbox-picture',
30016 action : 'picture',
30020 cls : 'btn btn-default',
30021 html : '<i class="fa fa-picture-o"></i>'
30027 cls : 'btn-group roo-upload-cropbox-rotate-right',
30028 action : 'rotate-right',
30032 cls : 'btn btn-default',
30033 html : '<i class="fa fa-repeat"></i>'
30041 cls : 'btn-group roo-upload-cropbox-rotate-left',
30042 action : 'rotate-left',
30046 cls : 'btn btn-default',
30047 html : '<i class="fa fa-undo"></i>'
30053 cls : 'btn-group roo-upload-cropbox-download',
30054 action : 'download',
30058 cls : 'btn btn-default',
30059 html : '<i class="fa fa-download"></i>'
30065 cls : 'btn-group roo-upload-cropbox-crop',
30070 cls : 'btn btn-default',
30071 html : '<i class="fa fa-crop"></i>'
30077 cls : 'btn-group roo-upload-cropbox-trash',
30082 cls : 'btn btn-default',
30083 html : '<i class="fa fa-trash"></i>'
30089 cls : 'btn-group roo-upload-cropbox-rotate-right',
30090 action : 'rotate-right',
30094 cls : 'btn btn-default',
30095 html : '<i class="fa fa-repeat"></i>'
30103 cls : 'btn-group roo-upload-cropbox-rotate-left',
30104 action : 'rotate-left',
30108 cls : 'btn btn-default',
30109 html : '<i class="fa fa-undo"></i>'
30115 cls : 'btn-group roo-upload-cropbox-rotate-right',
30116 action : 'rotate-right',
30120 cls : 'btn btn-default',
30121 html : '<i class="fa fa-repeat"></i>'
30134 * @class Roo.bootstrap.DocumentManager
30135 * @extends Roo.bootstrap.Component
30136 * Bootstrap DocumentManager class
30137 * @cfg {String} paramName default 'imageUpload'
30138 * @cfg {String} toolTipName default 'filename'
30139 * @cfg {String} method default POST
30140 * @cfg {String} url action url
30141 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30142 * @cfg {Boolean} multiple multiple upload default true
30143 * @cfg {Number} thumbSize default 300
30144 * @cfg {String} fieldLabel
30145 * @cfg {Number} labelWidth default 4
30146 * @cfg {String} labelAlign (left|top) default left
30147 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30148 * @cfg {Number} labellg set the width of label (1-12)
30149 * @cfg {Number} labelmd set the width of label (1-12)
30150 * @cfg {Number} labelsm set the width of label (1-12)
30151 * @cfg {Number} labelxs set the width of label (1-12)
30154 * Create a new DocumentManager
30155 * @param {Object} config The config object
30158 Roo.bootstrap.DocumentManager = function(config){
30159 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30162 this.delegates = [];
30167 * Fire when initial the DocumentManager
30168 * @param {Roo.bootstrap.DocumentManager} this
30173 * inspect selected file
30174 * @param {Roo.bootstrap.DocumentManager} this
30175 * @param {File} file
30180 * Fire when xhr load exception
30181 * @param {Roo.bootstrap.DocumentManager} this
30182 * @param {XMLHttpRequest} xhr
30184 "exception" : true,
30186 * @event afterupload
30187 * Fire when xhr load exception
30188 * @param {Roo.bootstrap.DocumentManager} this
30189 * @param {XMLHttpRequest} xhr
30191 "afterupload" : true,
30194 * prepare the form data
30195 * @param {Roo.bootstrap.DocumentManager} this
30196 * @param {Object} formData
30201 * Fire when remove the file
30202 * @param {Roo.bootstrap.DocumentManager} this
30203 * @param {Object} file
30208 * Fire after refresh the file
30209 * @param {Roo.bootstrap.DocumentManager} this
30214 * Fire after click the image
30215 * @param {Roo.bootstrap.DocumentManager} this
30216 * @param {Object} file
30221 * Fire when upload a image and editable set to true
30222 * @param {Roo.bootstrap.DocumentManager} this
30223 * @param {Object} file
30227 * @event beforeselectfile
30228 * Fire before select file
30229 * @param {Roo.bootstrap.DocumentManager} this
30231 "beforeselectfile" : true,
30234 * Fire before process file
30235 * @param {Roo.bootstrap.DocumentManager} this
30236 * @param {Object} file
30240 * @event previewrendered
30241 * Fire when preview rendered
30242 * @param {Roo.bootstrap.DocumentManager} this
30243 * @param {Object} file
30245 "previewrendered" : true,
30248 "previewResize" : true
30253 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30262 paramName : 'imageUpload',
30263 toolTipName : 'filename',
30266 labelAlign : 'left',
30276 getAutoCreate : function()
30278 var managerWidget = {
30280 cls : 'roo-document-manager',
30284 cls : 'roo-document-manager-selector',
30289 cls : 'roo-document-manager-uploader',
30293 cls : 'roo-document-manager-upload-btn',
30294 html : '<i class="fa fa-plus"></i>'
30305 cls : 'column col-md-12',
30310 if(this.fieldLabel.length){
30315 cls : 'column col-md-12',
30316 html : this.fieldLabel
30320 cls : 'column col-md-12',
30325 if(this.labelAlign == 'left'){
30330 html : this.fieldLabel
30339 if(this.labelWidth > 12){
30340 content[0].style = "width: " + this.labelWidth + 'px';
30343 if(this.labelWidth < 13 && this.labelmd == 0){
30344 this.labelmd = this.labelWidth;
30347 if(this.labellg > 0){
30348 content[0].cls += ' col-lg-' + this.labellg;
30349 content[1].cls += ' col-lg-' + (12 - this.labellg);
30352 if(this.labelmd > 0){
30353 content[0].cls += ' col-md-' + this.labelmd;
30354 content[1].cls += ' col-md-' + (12 - this.labelmd);
30357 if(this.labelsm > 0){
30358 content[0].cls += ' col-sm-' + this.labelsm;
30359 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30362 if(this.labelxs > 0){
30363 content[0].cls += ' col-xs-' + this.labelxs;
30364 content[1].cls += ' col-xs-' + (12 - this.labelxs);
30372 cls : 'row clearfix',
30380 initEvents : function()
30382 this.managerEl = this.el.select('.roo-document-manager', true).first();
30383 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30385 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30386 this.selectorEl.hide();
30389 this.selectorEl.attr('multiple', 'multiple');
30392 this.selectorEl.on('change', this.onFileSelected, this);
30394 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30395 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30397 this.uploader.on('click', this.onUploaderClick, this);
30399 this.renderProgressDialog();
30403 window.addEventListener("resize", function() { _this.refresh(); } );
30405 this.fireEvent('initial', this);
30408 renderProgressDialog : function()
30412 this.progressDialog = new Roo.bootstrap.Modal({
30413 cls : 'roo-document-manager-progress-dialog',
30414 allow_close : false,
30425 btnclick : function() {
30426 _this.uploadCancel();
30432 this.progressDialog.render(Roo.get(document.body));
30434 this.progress = new Roo.bootstrap.Progress({
30435 cls : 'roo-document-manager-progress',
30440 this.progress.render(this.progressDialog.getChildContainer());
30442 this.progressBar = new Roo.bootstrap.ProgressBar({
30443 cls : 'roo-document-manager-progress-bar',
30446 aria_valuemax : 12,
30450 this.progressBar.render(this.progress.getChildContainer());
30453 onUploaderClick : function(e)
30455 e.preventDefault();
30457 if(this.fireEvent('beforeselectfile', this) != false){
30458 this.selectorEl.dom.click();
30463 onFileSelected : function(e)
30465 e.preventDefault();
30467 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30471 Roo.each(this.selectorEl.dom.files, function(file){
30472 if(this.fireEvent('inspect', this, file) != false){
30473 this.files.push(file);
30483 this.selectorEl.dom.value = '';
30485 if(!this.files || !this.files.length){
30489 if(this.boxes > 0 && this.files.length > this.boxes){
30490 this.files = this.files.slice(0, this.boxes);
30493 this.uploader.show();
30495 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30496 this.uploader.hide();
30505 Roo.each(this.files, function(file){
30507 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30508 var f = this.renderPreview(file);
30513 if(file.type.indexOf('image') != -1){
30514 this.delegates.push(
30516 _this.process(file);
30517 }).createDelegate(this)
30525 _this.process(file);
30526 }).createDelegate(this)
30531 this.files = files;
30533 this.delegates = this.delegates.concat(docs);
30535 if(!this.delegates.length){
30540 this.progressBar.aria_valuemax = this.delegates.length;
30547 arrange : function()
30549 if(!this.delegates.length){
30550 this.progressDialog.hide();
30555 var delegate = this.delegates.shift();
30557 this.progressDialog.show();
30559 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30561 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30566 refresh : function()
30568 this.uploader.show();
30570 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30571 this.uploader.hide();
30574 Roo.isTouch ? this.closable(false) : this.closable(true);
30576 this.fireEvent('refresh', this);
30579 onRemove : function(e, el, o)
30581 e.preventDefault();
30583 this.fireEvent('remove', this, o);
30587 remove : function(o)
30591 Roo.each(this.files, function(file){
30592 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30601 this.files = files;
30608 Roo.each(this.files, function(file){
30613 file.target.remove();
30622 onClick : function(e, el, o)
30624 e.preventDefault();
30626 this.fireEvent('click', this, o);
30630 closable : function(closable)
30632 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30634 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30646 xhrOnLoad : function(xhr)
30648 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30652 if (xhr.readyState !== 4) {
30654 this.fireEvent('exception', this, xhr);
30658 var response = Roo.decode(xhr.responseText);
30660 if(!response.success){
30662 this.fireEvent('exception', this, xhr);
30666 var file = this.renderPreview(response.data);
30668 this.files.push(file);
30672 this.fireEvent('afterupload', this, xhr);
30676 xhrOnError : function(xhr)
30678 Roo.log('xhr on error');
30680 var response = Roo.decode(xhr.responseText);
30687 process : function(file)
30689 if(this.fireEvent('process', this, file) !== false){
30690 if(this.editable && file.type.indexOf('image') != -1){
30691 this.fireEvent('edit', this, file);
30695 this.uploadStart(file, false);
30702 uploadStart : function(file, crop)
30704 this.xhr = new XMLHttpRequest();
30706 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30711 file.xhr = this.xhr;
30713 this.managerEl.createChild({
30715 cls : 'roo-document-manager-loading',
30719 tooltip : file.name,
30720 cls : 'roo-document-manager-thumb',
30721 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30727 this.xhr.open(this.method, this.url, true);
30730 "Accept": "application/json",
30731 "Cache-Control": "no-cache",
30732 "X-Requested-With": "XMLHttpRequest"
30735 for (var headerName in headers) {
30736 var headerValue = headers[headerName];
30738 this.xhr.setRequestHeader(headerName, headerValue);
30744 this.xhr.onload = function()
30746 _this.xhrOnLoad(_this.xhr);
30749 this.xhr.onerror = function()
30751 _this.xhrOnError(_this.xhr);
30754 var formData = new FormData();
30756 formData.append('returnHTML', 'NO');
30759 formData.append('crop', crop);
30762 formData.append(this.paramName, file, file.name);
30769 if(this.fireEvent('prepare', this, formData, options) != false){
30771 if(options.manually){
30775 this.xhr.send(formData);
30779 this.uploadCancel();
30782 uploadCancel : function()
30788 this.delegates = [];
30790 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30797 renderPreview : function(file)
30799 if(typeof(file.target) != 'undefined' && file.target){
30803 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30805 var previewEl = this.managerEl.createChild({
30807 cls : 'roo-document-manager-preview',
30811 tooltip : file[this.toolTipName],
30812 cls : 'roo-document-manager-thumb',
30813 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30818 html : '<i class="fa fa-times-circle"></i>'
30823 var close = previewEl.select('button.close', true).first();
30825 close.on('click', this.onRemove, this, file);
30827 file.target = previewEl;
30829 var image = previewEl.select('img', true).first();
30833 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30835 image.on('click', this.onClick, this, file);
30837 this.fireEvent('previewrendered', this, file);
30843 onPreviewLoad : function(file, image)
30845 if(typeof(file.target) == 'undefined' || !file.target){
30849 var width = image.dom.naturalWidth || image.dom.width;
30850 var height = image.dom.naturalHeight || image.dom.height;
30852 if(!this.previewResize) {
30856 if(width > height){
30857 file.target.addClass('wide');
30861 file.target.addClass('tall');
30866 uploadFromSource : function(file, crop)
30868 this.xhr = new XMLHttpRequest();
30870 this.managerEl.createChild({
30872 cls : 'roo-document-manager-loading',
30876 tooltip : file.name,
30877 cls : 'roo-document-manager-thumb',
30878 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30884 this.xhr.open(this.method, this.url, true);
30887 "Accept": "application/json",
30888 "Cache-Control": "no-cache",
30889 "X-Requested-With": "XMLHttpRequest"
30892 for (var headerName in headers) {
30893 var headerValue = headers[headerName];
30895 this.xhr.setRequestHeader(headerName, headerValue);
30901 this.xhr.onload = function()
30903 _this.xhrOnLoad(_this.xhr);
30906 this.xhr.onerror = function()
30908 _this.xhrOnError(_this.xhr);
30911 var formData = new FormData();
30913 formData.append('returnHTML', 'NO');
30915 formData.append('crop', crop);
30917 if(typeof(file.filename) != 'undefined'){
30918 formData.append('filename', file.filename);
30921 if(typeof(file.mimetype) != 'undefined'){
30922 formData.append('mimetype', file.mimetype);
30927 if(this.fireEvent('prepare', this, formData) != false){
30928 this.xhr.send(formData);
30938 * @class Roo.bootstrap.DocumentViewer
30939 * @extends Roo.bootstrap.Component
30940 * Bootstrap DocumentViewer class
30941 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30942 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30945 * Create a new DocumentViewer
30946 * @param {Object} config The config object
30949 Roo.bootstrap.DocumentViewer = function(config){
30950 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30955 * Fire after initEvent
30956 * @param {Roo.bootstrap.DocumentViewer} this
30962 * @param {Roo.bootstrap.DocumentViewer} this
30967 * Fire after download button
30968 * @param {Roo.bootstrap.DocumentViewer} this
30973 * Fire after trash button
30974 * @param {Roo.bootstrap.DocumentViewer} this
30981 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30983 showDownload : true,
30987 getAutoCreate : function()
30991 cls : 'roo-document-viewer',
30995 cls : 'roo-document-viewer-body',
30999 cls : 'roo-document-viewer-thumb',
31003 cls : 'roo-document-viewer-image'
31011 cls : 'roo-document-viewer-footer',
31014 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31018 cls : 'btn-group roo-document-viewer-download',
31022 cls : 'btn btn-default',
31023 html : '<i class="fa fa-download"></i>'
31029 cls : 'btn-group roo-document-viewer-trash',
31033 cls : 'btn btn-default',
31034 html : '<i class="fa fa-trash"></i>'
31047 initEvents : function()
31049 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31050 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31052 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31053 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31055 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31056 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31058 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31059 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31061 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31062 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31064 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31065 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31067 this.bodyEl.on('click', this.onClick, this);
31068 this.downloadBtn.on('click', this.onDownload, this);
31069 this.trashBtn.on('click', this.onTrash, this);
31071 this.downloadBtn.hide();
31072 this.trashBtn.hide();
31074 if(this.showDownload){
31075 this.downloadBtn.show();
31078 if(this.showTrash){
31079 this.trashBtn.show();
31082 if(!this.showDownload && !this.showTrash) {
31083 this.footerEl.hide();
31088 initial : function()
31090 this.fireEvent('initial', this);
31094 onClick : function(e)
31096 e.preventDefault();
31098 this.fireEvent('click', this);
31101 onDownload : function(e)
31103 e.preventDefault();
31105 this.fireEvent('download', this);
31108 onTrash : function(e)
31110 e.preventDefault();
31112 this.fireEvent('trash', this);
31124 * @class Roo.bootstrap.NavProgressBar
31125 * @extends Roo.bootstrap.Component
31126 * Bootstrap NavProgressBar class
31129 * Create a new nav progress bar
31130 * @param {Object} config The config object
31133 Roo.bootstrap.NavProgressBar = function(config){
31134 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31136 this.bullets = this.bullets || [];
31138 // Roo.bootstrap.NavProgressBar.register(this);
31142 * Fires when the active item changes
31143 * @param {Roo.bootstrap.NavProgressBar} this
31144 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31145 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31152 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31157 getAutoCreate : function()
31159 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31163 cls : 'roo-navigation-bar-group',
31167 cls : 'roo-navigation-top-bar'
31171 cls : 'roo-navigation-bullets-bar',
31175 cls : 'roo-navigation-bar'
31182 cls : 'roo-navigation-bottom-bar'
31192 initEvents: function()
31197 onRender : function(ct, position)
31199 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31201 if(this.bullets.length){
31202 Roo.each(this.bullets, function(b){
31211 addItem : function(cfg)
31213 var item = new Roo.bootstrap.NavProgressItem(cfg);
31215 item.parentId = this.id;
31216 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31219 var top = new Roo.bootstrap.Element({
31221 cls : 'roo-navigation-bar-text'
31224 var bottom = new Roo.bootstrap.Element({
31226 cls : 'roo-navigation-bar-text'
31229 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31230 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31232 var topText = new Roo.bootstrap.Element({
31234 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31237 var bottomText = new Roo.bootstrap.Element({
31239 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31242 topText.onRender(top.el, null);
31243 bottomText.onRender(bottom.el, null);
31246 item.bottomEl = bottom;
31249 this.barItems.push(item);
31254 getActive : function()
31256 var active = false;
31258 Roo.each(this.barItems, function(v){
31260 if (!v.isActive()) {
31272 setActiveItem : function(item)
31276 Roo.each(this.barItems, function(v){
31277 if (v.rid == item.rid) {
31281 if (v.isActive()) {
31282 v.setActive(false);
31287 item.setActive(true);
31289 this.fireEvent('changed', this, item, prev);
31292 getBarItem: function(rid)
31296 Roo.each(this.barItems, function(e) {
31297 if (e.rid != rid) {
31308 indexOfItem : function(item)
31312 Roo.each(this.barItems, function(v, i){
31314 if (v.rid != item.rid) {
31325 setActiveNext : function()
31327 var i = this.indexOfItem(this.getActive());
31329 if (i > this.barItems.length) {
31333 this.setActiveItem(this.barItems[i+1]);
31336 setActivePrev : function()
31338 var i = this.indexOfItem(this.getActive());
31344 this.setActiveItem(this.barItems[i-1]);
31347 format : function()
31349 if(!this.barItems.length){
31353 var width = 100 / this.barItems.length;
31355 Roo.each(this.barItems, function(i){
31356 i.el.setStyle('width', width + '%');
31357 i.topEl.el.setStyle('width', width + '%');
31358 i.bottomEl.el.setStyle('width', width + '%');
31367 * Nav Progress Item
31372 * @class Roo.bootstrap.NavProgressItem
31373 * @extends Roo.bootstrap.Component
31374 * Bootstrap NavProgressItem class
31375 * @cfg {String} rid the reference id
31376 * @cfg {Boolean} active (true|false) Is item active default false
31377 * @cfg {Boolean} disabled (true|false) Is item active default false
31378 * @cfg {String} html
31379 * @cfg {String} position (top|bottom) text position default bottom
31380 * @cfg {String} icon show icon instead of number
31383 * Create a new NavProgressItem
31384 * @param {Object} config The config object
31386 Roo.bootstrap.NavProgressItem = function(config){
31387 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31392 * The raw click event for the entire grid.
31393 * @param {Roo.bootstrap.NavProgressItem} this
31394 * @param {Roo.EventObject} e
31401 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
31407 position : 'bottom',
31410 getAutoCreate : function()
31412 var iconCls = 'roo-navigation-bar-item-icon';
31414 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31418 cls: 'roo-navigation-bar-item',
31428 cfg.cls += ' active';
31431 cfg.cls += ' disabled';
31437 disable : function()
31439 this.setDisabled(true);
31442 enable : function()
31444 this.setDisabled(false);
31447 initEvents: function()
31449 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31451 this.iconEl.on('click', this.onClick, this);
31454 onClick : function(e)
31456 e.preventDefault();
31462 if(this.fireEvent('click', this, e) === false){
31466 this.parent().setActiveItem(this);
31469 isActive: function ()
31471 return this.active;
31474 setActive : function(state)
31476 if(this.active == state){
31480 this.active = state;
31483 this.el.addClass('active');
31487 this.el.removeClass('active');
31492 setDisabled : function(state)
31494 if(this.disabled == state){
31498 this.disabled = state;
31501 this.el.addClass('disabled');
31505 this.el.removeClass('disabled');
31508 tooltipEl : function()
31510 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31523 * @class Roo.bootstrap.FieldLabel
31524 * @extends Roo.bootstrap.Component
31525 * Bootstrap FieldLabel class
31526 * @cfg {String} html contents of the element
31527 * @cfg {String} tag tag of the element default label
31528 * @cfg {String} cls class of the element
31529 * @cfg {String} target label target
31530 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31531 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31532 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31533 * @cfg {String} iconTooltip default "This field is required"
31534 * @cfg {String} indicatorpos (left|right) default left
31537 * Create a new FieldLabel
31538 * @param {Object} config The config object
31541 Roo.bootstrap.FieldLabel = function(config){
31542 Roo.bootstrap.Element.superclass.constructor.call(this, config);
31547 * Fires after the field has been marked as invalid.
31548 * @param {Roo.form.FieldLabel} this
31549 * @param {String} msg The validation message
31554 * Fires after the field has been validated with no errors.
31555 * @param {Roo.form.FieldLabel} this
31561 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
31568 invalidClass : 'has-warning',
31569 validClass : 'has-success',
31570 iconTooltip : 'This field is required',
31571 indicatorpos : 'left',
31573 getAutoCreate : function(){
31576 if (!this.allowBlank) {
31582 cls : 'roo-bootstrap-field-label ' + this.cls,
31587 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31588 tooltip : this.iconTooltip
31597 if(this.indicatorpos == 'right'){
31600 cls : 'roo-bootstrap-field-label ' + this.cls,
31609 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31610 tooltip : this.iconTooltip
31619 initEvents: function()
31621 Roo.bootstrap.Element.superclass.initEvents.call(this);
31623 this.indicator = this.indicatorEl();
31625 if(this.indicator){
31626 this.indicator.removeClass('visible');
31627 this.indicator.addClass('invisible');
31630 Roo.bootstrap.FieldLabel.register(this);
31633 indicatorEl : function()
31635 var indicator = this.el.select('i.roo-required-indicator',true).first();
31646 * Mark this field as valid
31648 markValid : function()
31650 if(this.indicator){
31651 this.indicator.removeClass('visible');
31652 this.indicator.addClass('invisible');
31654 if (Roo.bootstrap.version == 3) {
31655 this.el.removeClass(this.invalidClass);
31656 this.el.addClass(this.validClass);
31658 this.el.removeClass('is-invalid');
31659 this.el.addClass('is-valid');
31663 this.fireEvent('valid', this);
31667 * Mark this field as invalid
31668 * @param {String} msg The validation message
31670 markInvalid : function(msg)
31672 if(this.indicator){
31673 this.indicator.removeClass('invisible');
31674 this.indicator.addClass('visible');
31676 if (Roo.bootstrap.version == 3) {
31677 this.el.removeClass(this.validClass);
31678 this.el.addClass(this.invalidClass);
31680 this.el.removeClass('is-valid');
31681 this.el.addClass('is-invalid');
31685 this.fireEvent('invalid', this, msg);
31691 Roo.apply(Roo.bootstrap.FieldLabel, {
31696 * register a FieldLabel Group
31697 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31699 register : function(label)
31701 if(this.groups.hasOwnProperty(label.target)){
31705 this.groups[label.target] = label;
31709 * fetch a FieldLabel Group based on the target
31710 * @param {string} target
31711 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31713 get: function(target) {
31714 if (typeof(this.groups[target]) == 'undefined') {
31718 return this.groups[target] ;
31727 * page DateSplitField.
31733 * @class Roo.bootstrap.DateSplitField
31734 * @extends Roo.bootstrap.Component
31735 * Bootstrap DateSplitField class
31736 * @cfg {string} fieldLabel - the label associated
31737 * @cfg {Number} labelWidth set the width of label (0-12)
31738 * @cfg {String} labelAlign (top|left)
31739 * @cfg {Boolean} dayAllowBlank (true|false) default false
31740 * @cfg {Boolean} monthAllowBlank (true|false) default false
31741 * @cfg {Boolean} yearAllowBlank (true|false) default false
31742 * @cfg {string} dayPlaceholder
31743 * @cfg {string} monthPlaceholder
31744 * @cfg {string} yearPlaceholder
31745 * @cfg {string} dayFormat default 'd'
31746 * @cfg {string} monthFormat default 'm'
31747 * @cfg {string} yearFormat default 'Y'
31748 * @cfg {Number} labellg set the width of label (1-12)
31749 * @cfg {Number} labelmd set the width of label (1-12)
31750 * @cfg {Number} labelsm set the width of label (1-12)
31751 * @cfg {Number} labelxs set the width of label (1-12)
31755 * Create a new DateSplitField
31756 * @param {Object} config The config object
31759 Roo.bootstrap.DateSplitField = function(config){
31760 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31766 * getting the data of years
31767 * @param {Roo.bootstrap.DateSplitField} this
31768 * @param {Object} years
31773 * getting the data of days
31774 * @param {Roo.bootstrap.DateSplitField} this
31775 * @param {Object} days
31780 * Fires after the field has been marked as invalid.
31781 * @param {Roo.form.Field} this
31782 * @param {String} msg The validation message
31787 * Fires after the field has been validated with no errors.
31788 * @param {Roo.form.Field} this
31794 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31797 labelAlign : 'top',
31799 dayAllowBlank : false,
31800 monthAllowBlank : false,
31801 yearAllowBlank : false,
31802 dayPlaceholder : '',
31803 monthPlaceholder : '',
31804 yearPlaceholder : '',
31808 isFormField : true,
31814 getAutoCreate : function()
31818 cls : 'row roo-date-split-field-group',
31823 cls : 'form-hidden-field roo-date-split-field-group-value',
31829 var labelCls = 'col-md-12';
31830 var contentCls = 'col-md-4';
31832 if(this.fieldLabel){
31836 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31840 html : this.fieldLabel
31845 if(this.labelAlign == 'left'){
31847 if(this.labelWidth > 12){
31848 label.style = "width: " + this.labelWidth + 'px';
31851 if(this.labelWidth < 13 && this.labelmd == 0){
31852 this.labelmd = this.labelWidth;
31855 if(this.labellg > 0){
31856 labelCls = ' col-lg-' + this.labellg;
31857 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31860 if(this.labelmd > 0){
31861 labelCls = ' col-md-' + this.labelmd;
31862 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31865 if(this.labelsm > 0){
31866 labelCls = ' col-sm-' + this.labelsm;
31867 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31870 if(this.labelxs > 0){
31871 labelCls = ' col-xs-' + this.labelxs;
31872 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31876 label.cls += ' ' + labelCls;
31878 cfg.cn.push(label);
31881 Roo.each(['day', 'month', 'year'], function(t){
31884 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31891 inputEl: function ()
31893 return this.el.select('.roo-date-split-field-group-value', true).first();
31896 onRender : function(ct, position)
31900 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31902 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31904 this.dayField = new Roo.bootstrap.ComboBox({
31905 allowBlank : this.dayAllowBlank,
31906 alwaysQuery : true,
31907 displayField : 'value',
31910 forceSelection : true,
31912 placeholder : this.dayPlaceholder,
31913 selectOnFocus : true,
31914 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31915 triggerAction : 'all',
31917 valueField : 'value',
31918 store : new Roo.data.SimpleStore({
31919 data : (function() {
31921 _this.fireEvent('days', _this, days);
31924 fields : [ 'value' ]
31927 select : function (_self, record, index)
31929 _this.setValue(_this.getValue());
31934 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31936 this.monthField = new Roo.bootstrap.MonthField({
31937 after : '<i class=\"fa fa-calendar\"></i>',
31938 allowBlank : this.monthAllowBlank,
31939 placeholder : this.monthPlaceholder,
31942 render : function (_self)
31944 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31945 e.preventDefault();
31949 select : function (_self, oldvalue, newvalue)
31951 _this.setValue(_this.getValue());
31956 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31958 this.yearField = new Roo.bootstrap.ComboBox({
31959 allowBlank : this.yearAllowBlank,
31960 alwaysQuery : true,
31961 displayField : 'value',
31964 forceSelection : true,
31966 placeholder : this.yearPlaceholder,
31967 selectOnFocus : true,
31968 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31969 triggerAction : 'all',
31971 valueField : 'value',
31972 store : new Roo.data.SimpleStore({
31973 data : (function() {
31975 _this.fireEvent('years', _this, years);
31978 fields : [ 'value' ]
31981 select : function (_self, record, index)
31983 _this.setValue(_this.getValue());
31988 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31991 setValue : function(v, format)
31993 this.inputEl.dom.value = v;
31995 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31997 var d = Date.parseDate(v, f);
32004 this.setDay(d.format(this.dayFormat));
32005 this.setMonth(d.format(this.monthFormat));
32006 this.setYear(d.format(this.yearFormat));
32013 setDay : function(v)
32015 this.dayField.setValue(v);
32016 this.inputEl.dom.value = this.getValue();
32021 setMonth : function(v)
32023 this.monthField.setValue(v, true);
32024 this.inputEl.dom.value = this.getValue();
32029 setYear : function(v)
32031 this.yearField.setValue(v);
32032 this.inputEl.dom.value = this.getValue();
32037 getDay : function()
32039 return this.dayField.getValue();
32042 getMonth : function()
32044 return this.monthField.getValue();
32047 getYear : function()
32049 return this.yearField.getValue();
32052 getValue : function()
32054 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32056 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32066 this.inputEl.dom.value = '';
32071 validate : function()
32073 var d = this.dayField.validate();
32074 var m = this.monthField.validate();
32075 var y = this.yearField.validate();
32080 (!this.dayAllowBlank && !d) ||
32081 (!this.monthAllowBlank && !m) ||
32082 (!this.yearAllowBlank && !y)
32087 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32096 this.markInvalid();
32101 markValid : function()
32104 var label = this.el.select('label', true).first();
32105 var icon = this.el.select('i.fa-star', true).first();
32111 this.fireEvent('valid', this);
32115 * Mark this field as invalid
32116 * @param {String} msg The validation message
32118 markInvalid : function(msg)
32121 var label = this.el.select('label', true).first();
32122 var icon = this.el.select('i.fa-star', true).first();
32124 if(label && !icon){
32125 this.el.select('.roo-date-split-field-label', true).createChild({
32127 cls : 'text-danger fa fa-lg fa-star',
32128 tooltip : 'This field is required',
32129 style : 'margin-right:5px;'
32133 this.fireEvent('invalid', this, msg);
32136 clearInvalid : function()
32138 var label = this.el.select('label', true).first();
32139 var icon = this.el.select('i.fa-star', true).first();
32145 this.fireEvent('valid', this);
32148 getName: function()
32158 * http://masonry.desandro.com
32160 * The idea is to render all the bricks based on vertical width...
32162 * The original code extends 'outlayer' - we might need to use that....
32168 * @class Roo.bootstrap.LayoutMasonry
32169 * @extends Roo.bootstrap.Component
32170 * Bootstrap Layout Masonry class
32173 * Create a new Element
32174 * @param {Object} config The config object
32177 Roo.bootstrap.LayoutMasonry = function(config){
32179 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32183 Roo.bootstrap.LayoutMasonry.register(this);
32189 * Fire after layout the items
32190 * @param {Roo.bootstrap.LayoutMasonry} this
32191 * @param {Roo.EventObject} e
32198 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32201 * @cfg {Boolean} isLayoutInstant = no animation?
32203 isLayoutInstant : false, // needed?
32206 * @cfg {Number} boxWidth width of the columns
32211 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32216 * @cfg {Number} padWidth padding below box..
32221 * @cfg {Number} gutter gutter width..
32226 * @cfg {Number} maxCols maximum number of columns
32232 * @cfg {Boolean} isAutoInitial defalut true
32234 isAutoInitial : true,
32239 * @cfg {Boolean} isHorizontal defalut false
32241 isHorizontal : false,
32243 currentSize : null,
32249 bricks: null, //CompositeElement
32253 _isLayoutInited : false,
32255 // isAlternative : false, // only use for vertical layout...
32258 * @cfg {Number} alternativePadWidth padding below box..
32260 alternativePadWidth : 50,
32262 selectedBrick : [],
32264 getAutoCreate : function(){
32266 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32270 cls: 'blog-masonary-wrapper ' + this.cls,
32272 cls : 'mas-boxes masonary'
32279 getChildContainer: function( )
32281 if (this.boxesEl) {
32282 return this.boxesEl;
32285 this.boxesEl = this.el.select('.mas-boxes').first();
32287 return this.boxesEl;
32291 initEvents : function()
32295 if(this.isAutoInitial){
32296 Roo.log('hook children rendered');
32297 this.on('childrenrendered', function() {
32298 Roo.log('children rendered');
32304 initial : function()
32306 this.selectedBrick = [];
32308 this.currentSize = this.el.getBox(true);
32310 Roo.EventManager.onWindowResize(this.resize, this);
32312 if(!this.isAutoInitial){
32320 //this.layout.defer(500,this);
32324 resize : function()
32326 var cs = this.el.getBox(true);
32329 this.currentSize.width == cs.width &&
32330 this.currentSize.x == cs.x &&
32331 this.currentSize.height == cs.height &&
32332 this.currentSize.y == cs.y
32334 Roo.log("no change in with or X or Y");
32338 this.currentSize = cs;
32344 layout : function()
32346 this._resetLayout();
32348 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32350 this.layoutItems( isInstant );
32352 this._isLayoutInited = true;
32354 this.fireEvent('layout', this);
32358 _resetLayout : function()
32360 if(this.isHorizontal){
32361 this.horizontalMeasureColumns();
32365 this.verticalMeasureColumns();
32369 verticalMeasureColumns : function()
32371 this.getContainerWidth();
32373 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32374 // this.colWidth = Math.floor(this.containerWidth * 0.8);
32378 var boxWidth = this.boxWidth + this.padWidth;
32380 if(this.containerWidth < this.boxWidth){
32381 boxWidth = this.containerWidth
32384 var containerWidth = this.containerWidth;
32386 var cols = Math.floor(containerWidth / boxWidth);
32388 this.cols = Math.max( cols, 1 );
32390 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32392 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32394 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32396 this.colWidth = boxWidth + avail - this.padWidth;
32398 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32399 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
32402 horizontalMeasureColumns : function()
32404 this.getContainerWidth();
32406 var boxWidth = this.boxWidth;
32408 if(this.containerWidth < boxWidth){
32409 boxWidth = this.containerWidth;
32412 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32414 this.el.setHeight(boxWidth);
32418 getContainerWidth : function()
32420 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32423 layoutItems : function( isInstant )
32425 Roo.log(this.bricks);
32427 var items = Roo.apply([], this.bricks);
32429 if(this.isHorizontal){
32430 this._horizontalLayoutItems( items , isInstant );
32434 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32435 // this._verticalAlternativeLayoutItems( items , isInstant );
32439 this._verticalLayoutItems( items , isInstant );
32443 _verticalLayoutItems : function ( items , isInstant)
32445 if ( !items || !items.length ) {
32450 ['xs', 'xs', 'xs', 'tall'],
32451 ['xs', 'xs', 'tall'],
32452 ['xs', 'xs', 'sm'],
32453 ['xs', 'xs', 'xs'],
32459 ['sm', 'xs', 'xs'],
32463 ['tall', 'xs', 'xs', 'xs'],
32464 ['tall', 'xs', 'xs'],
32476 Roo.each(items, function(item, k){
32478 switch (item.size) {
32479 // these layouts take up a full box,
32490 boxes.push([item]);
32513 var filterPattern = function(box, length)
32521 var pattern = box.slice(0, length);
32525 Roo.each(pattern, function(i){
32526 format.push(i.size);
32529 Roo.each(standard, function(s){
32531 if(String(s) != String(format)){
32540 if(!match && length == 1){
32545 filterPattern(box, length - 1);
32549 queue.push(pattern);
32551 box = box.slice(length, box.length);
32553 filterPattern(box, 4);
32559 Roo.each(boxes, function(box, k){
32565 if(box.length == 1){
32570 filterPattern(box, 4);
32574 this._processVerticalLayoutQueue( queue, isInstant );
32578 // _verticalAlternativeLayoutItems : function( items , isInstant )
32580 // if ( !items || !items.length ) {
32584 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
32588 _horizontalLayoutItems : function ( items , isInstant)
32590 if ( !items || !items.length || items.length < 3) {
32596 var eItems = items.slice(0, 3);
32598 items = items.slice(3, items.length);
32601 ['xs', 'xs', 'xs', 'wide'],
32602 ['xs', 'xs', 'wide'],
32603 ['xs', 'xs', 'sm'],
32604 ['xs', 'xs', 'xs'],
32610 ['sm', 'xs', 'xs'],
32614 ['wide', 'xs', 'xs', 'xs'],
32615 ['wide', 'xs', 'xs'],
32628 Roo.each(items, function(item, k){
32630 switch (item.size) {
32641 boxes.push([item]);
32665 var filterPattern = function(box, length)
32673 var pattern = box.slice(0, length);
32677 Roo.each(pattern, function(i){
32678 format.push(i.size);
32681 Roo.each(standard, function(s){
32683 if(String(s) != String(format)){
32692 if(!match && length == 1){
32697 filterPattern(box, length - 1);
32701 queue.push(pattern);
32703 box = box.slice(length, box.length);
32705 filterPattern(box, 4);
32711 Roo.each(boxes, function(box, k){
32717 if(box.length == 1){
32722 filterPattern(box, 4);
32729 var pos = this.el.getBox(true);
32733 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32735 var hit_end = false;
32737 Roo.each(queue, function(box){
32741 Roo.each(box, function(b){
32743 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32753 Roo.each(box, function(b){
32755 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32758 mx = Math.max(mx, b.x);
32762 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32766 Roo.each(box, function(b){
32768 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32782 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32785 /** Sets position of item in DOM
32786 * @param {Element} item
32787 * @param {Number} x - horizontal position
32788 * @param {Number} y - vertical position
32789 * @param {Boolean} isInstant - disables transitions
32791 _processVerticalLayoutQueue : function( queue, isInstant )
32793 var pos = this.el.getBox(true);
32798 for (var i = 0; i < this.cols; i++){
32802 Roo.each(queue, function(box, k){
32804 var col = k % this.cols;
32806 Roo.each(box, function(b,kk){
32808 b.el.position('absolute');
32810 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32811 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32813 if(b.size == 'md-left' || b.size == 'md-right'){
32814 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32815 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32818 b.el.setWidth(width);
32819 b.el.setHeight(height);
32821 b.el.select('iframe',true).setSize(width,height);
32825 for (var i = 0; i < this.cols; i++){
32827 if(maxY[i] < maxY[col]){
32832 col = Math.min(col, i);
32836 x = pos.x + col * (this.colWidth + this.padWidth);
32840 var positions = [];
32842 switch (box.length){
32844 positions = this.getVerticalOneBoxColPositions(x, y, box);
32847 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32850 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32853 positions = this.getVerticalFourBoxColPositions(x, y, box);
32859 Roo.each(box, function(b,kk){
32861 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32863 var sz = b.el.getSize();
32865 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32873 for (var i = 0; i < this.cols; i++){
32874 mY = Math.max(mY, maxY[i]);
32877 this.el.setHeight(mY - pos.y);
32881 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32883 // var pos = this.el.getBox(true);
32886 // var maxX = pos.right;
32888 // var maxHeight = 0;
32890 // Roo.each(items, function(item, k){
32894 // item.el.position('absolute');
32896 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32898 // item.el.setWidth(width);
32900 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32902 // item.el.setHeight(height);
32905 // item.el.setXY([x, y], isInstant ? false : true);
32907 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32910 // y = y + height + this.alternativePadWidth;
32912 // maxHeight = maxHeight + height + this.alternativePadWidth;
32916 // this.el.setHeight(maxHeight);
32920 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32922 var pos = this.el.getBox(true);
32927 var maxX = pos.right;
32929 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32931 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32933 Roo.each(queue, function(box, k){
32935 Roo.each(box, function(b, kk){
32937 b.el.position('absolute');
32939 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32940 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32942 if(b.size == 'md-left' || b.size == 'md-right'){
32943 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32944 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32947 b.el.setWidth(width);
32948 b.el.setHeight(height);
32956 var positions = [];
32958 switch (box.length){
32960 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32963 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32966 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32969 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32975 Roo.each(box, function(b,kk){
32977 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32979 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32987 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32989 Roo.each(eItems, function(b,k){
32991 b.size = (k == 0) ? 'sm' : 'xs';
32992 b.x = (k == 0) ? 2 : 1;
32993 b.y = (k == 0) ? 2 : 1;
32995 b.el.position('absolute');
32997 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32999 b.el.setWidth(width);
33001 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33003 b.el.setHeight(height);
33007 var positions = [];
33010 x : maxX - this.unitWidth * 2 - this.gutter,
33015 x : maxX - this.unitWidth,
33016 y : minY + (this.unitWidth + this.gutter) * 2
33020 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33024 Roo.each(eItems, function(b,k){
33026 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33032 getVerticalOneBoxColPositions : function(x, y, box)
33036 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33038 if(box[0].size == 'md-left'){
33042 if(box[0].size == 'md-right'){
33047 x : x + (this.unitWidth + this.gutter) * rand,
33054 getVerticalTwoBoxColPositions : function(x, y, box)
33058 if(box[0].size == 'xs'){
33062 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33066 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33080 x : x + (this.unitWidth + this.gutter) * 2,
33081 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33088 getVerticalThreeBoxColPositions : function(x, y, box)
33092 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33100 x : x + (this.unitWidth + this.gutter) * 1,
33105 x : x + (this.unitWidth + this.gutter) * 2,
33113 if(box[0].size == 'xs' && box[1].size == 'xs'){
33122 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33126 x : x + (this.unitWidth + this.gutter) * 1,
33140 x : x + (this.unitWidth + this.gutter) * 2,
33145 x : x + (this.unitWidth + this.gutter) * 2,
33146 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33153 getVerticalFourBoxColPositions : function(x, y, box)
33157 if(box[0].size == 'xs'){
33166 y : y + (this.unitHeight + this.gutter) * 1
33171 y : y + (this.unitHeight + this.gutter) * 2
33175 x : x + (this.unitWidth + this.gutter) * 1,
33189 x : x + (this.unitWidth + this.gutter) * 2,
33194 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33195 y : y + (this.unitHeight + this.gutter) * 1
33199 x : x + (this.unitWidth + this.gutter) * 2,
33200 y : y + (this.unitWidth + this.gutter) * 2
33207 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33211 if(box[0].size == 'md-left'){
33213 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33220 if(box[0].size == 'md-right'){
33222 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33223 y : minY + (this.unitWidth + this.gutter) * 1
33229 var rand = Math.floor(Math.random() * (4 - box[0].y));
33232 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33233 y : minY + (this.unitWidth + this.gutter) * rand
33240 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33244 if(box[0].size == 'xs'){
33247 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33252 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33253 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33261 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33266 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33267 y : minY + (this.unitWidth + this.gutter) * 2
33274 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33278 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33281 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33286 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33287 y : minY + (this.unitWidth + this.gutter) * 1
33291 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33292 y : minY + (this.unitWidth + this.gutter) * 2
33299 if(box[0].size == 'xs' && box[1].size == 'xs'){
33302 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33307 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33312 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33313 y : minY + (this.unitWidth + this.gutter) * 1
33321 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33326 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33327 y : minY + (this.unitWidth + this.gutter) * 2
33331 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33332 y : minY + (this.unitWidth + this.gutter) * 2
33339 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33343 if(box[0].size == 'xs'){
33346 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33351 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33356 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),
33361 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33362 y : minY + (this.unitWidth + this.gutter) * 1
33370 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33375 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33376 y : minY + (this.unitWidth + this.gutter) * 2
33380 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33381 y : minY + (this.unitWidth + this.gutter) * 2
33385 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),
33386 y : minY + (this.unitWidth + this.gutter) * 2
33394 * remove a Masonry Brick
33395 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33397 removeBrick : function(brick_id)
33403 for (var i = 0; i<this.bricks.length; i++) {
33404 if (this.bricks[i].id == brick_id) {
33405 this.bricks.splice(i,1);
33406 this.el.dom.removeChild(Roo.get(brick_id).dom);
33413 * adds a Masonry Brick
33414 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33416 addBrick : function(cfg)
33418 var cn = new Roo.bootstrap.MasonryBrick(cfg);
33419 //this.register(cn);
33420 cn.parentId = this.id;
33421 cn.render(this.el);
33426 * register a Masonry Brick
33427 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33430 register : function(brick)
33432 this.bricks.push(brick);
33433 brick.masonryId = this.id;
33437 * clear all the Masonry Brick
33439 clearAll : function()
33442 //this.getChildContainer().dom.innerHTML = "";
33443 this.el.dom.innerHTML = '';
33446 getSelected : function()
33448 if (!this.selectedBrick) {
33452 return this.selectedBrick;
33456 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33460 * register a Masonry Layout
33461 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33464 register : function(layout)
33466 this.groups[layout.id] = layout;
33469 * fetch a Masonry Layout based on the masonry layout ID
33470 * @param {string} the masonry layout to add
33471 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33474 get: function(layout_id) {
33475 if (typeof(this.groups[layout_id]) == 'undefined') {
33478 return this.groups[layout_id] ;
33490 * http://masonry.desandro.com
33492 * The idea is to render all the bricks based on vertical width...
33494 * The original code extends 'outlayer' - we might need to use that....
33500 * @class Roo.bootstrap.LayoutMasonryAuto
33501 * @extends Roo.bootstrap.Component
33502 * Bootstrap Layout Masonry class
33505 * Create a new Element
33506 * @param {Object} config The config object
33509 Roo.bootstrap.LayoutMasonryAuto = function(config){
33510 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33513 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
33516 * @cfg {Boolean} isFitWidth - resize the width..
33518 isFitWidth : false, // options..
33520 * @cfg {Boolean} isOriginLeft = left align?
33522 isOriginLeft : true,
33524 * @cfg {Boolean} isOriginTop = top align?
33526 isOriginTop : false,
33528 * @cfg {Boolean} isLayoutInstant = no animation?
33530 isLayoutInstant : false, // needed?
33532 * @cfg {Boolean} isResizingContainer = not sure if this is used..
33534 isResizingContainer : true,
33536 * @cfg {Number} columnWidth width of the columns
33542 * @cfg {Number} maxCols maximum number of columns
33547 * @cfg {Number} padHeight padding below box..
33553 * @cfg {Boolean} isAutoInitial defalut true
33556 isAutoInitial : true,
33562 initialColumnWidth : 0,
33563 currentSize : null,
33565 colYs : null, // array.
33572 bricks: null, //CompositeElement
33573 cols : 0, // array?
33574 // element : null, // wrapped now this.el
33575 _isLayoutInited : null,
33578 getAutoCreate : function(){
33582 cls: 'blog-masonary-wrapper ' + this.cls,
33584 cls : 'mas-boxes masonary'
33591 getChildContainer: function( )
33593 if (this.boxesEl) {
33594 return this.boxesEl;
33597 this.boxesEl = this.el.select('.mas-boxes').first();
33599 return this.boxesEl;
33603 initEvents : function()
33607 if(this.isAutoInitial){
33608 Roo.log('hook children rendered');
33609 this.on('childrenrendered', function() {
33610 Roo.log('children rendered');
33617 initial : function()
33619 this.reloadItems();
33621 this.currentSize = this.el.getBox(true);
33623 /// was window resize... - let's see if this works..
33624 Roo.EventManager.onWindowResize(this.resize, this);
33626 if(!this.isAutoInitial){
33631 this.layout.defer(500,this);
33634 reloadItems: function()
33636 this.bricks = this.el.select('.masonry-brick', true);
33638 this.bricks.each(function(b) {
33639 //Roo.log(b.getSize());
33640 if (!b.attr('originalwidth')) {
33641 b.attr('originalwidth', b.getSize().width);
33646 Roo.log(this.bricks.elements.length);
33649 resize : function()
33652 var cs = this.el.getBox(true);
33654 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33655 Roo.log("no change in with or X");
33658 this.currentSize = cs;
33662 layout : function()
33665 this._resetLayout();
33666 //this._manageStamps();
33668 // don't animate first layout
33669 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33670 this.layoutItems( isInstant );
33672 // flag for initalized
33673 this._isLayoutInited = true;
33676 layoutItems : function( isInstant )
33678 //var items = this._getItemsForLayout( this.items );
33679 // original code supports filtering layout items.. we just ignore it..
33681 this._layoutItems( this.bricks , isInstant );
33683 this._postLayout();
33685 _layoutItems : function ( items , isInstant)
33687 //this.fireEvent( 'layout', this, items );
33690 if ( !items || !items.elements.length ) {
33691 // no items, emit event with empty array
33696 items.each(function(item) {
33697 Roo.log("layout item");
33699 // get x/y object from method
33700 var position = this._getItemLayoutPosition( item );
33702 position.item = item;
33703 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33704 queue.push( position );
33707 this._processLayoutQueue( queue );
33709 /** Sets position of item in DOM
33710 * @param {Element} item
33711 * @param {Number} x - horizontal position
33712 * @param {Number} y - vertical position
33713 * @param {Boolean} isInstant - disables transitions
33715 _processLayoutQueue : function( queue )
33717 for ( var i=0, len = queue.length; i < len; i++ ) {
33718 var obj = queue[i];
33719 obj.item.position('absolute');
33720 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33726 * Any logic you want to do after each layout,
33727 * i.e. size the container
33729 _postLayout : function()
33731 this.resizeContainer();
33734 resizeContainer : function()
33736 if ( !this.isResizingContainer ) {
33739 var size = this._getContainerSize();
33741 this.el.setSize(size.width,size.height);
33742 this.boxesEl.setSize(size.width,size.height);
33748 _resetLayout : function()
33750 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33751 this.colWidth = this.el.getWidth();
33752 //this.gutter = this.el.getWidth();
33754 this.measureColumns();
33760 this.colYs.push( 0 );
33766 measureColumns : function()
33768 this.getContainerWidth();
33769 // if columnWidth is 0, default to outerWidth of first item
33770 if ( !this.columnWidth ) {
33771 var firstItem = this.bricks.first();
33772 Roo.log(firstItem);
33773 this.columnWidth = this.containerWidth;
33774 if (firstItem && firstItem.attr('originalwidth') ) {
33775 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33777 // columnWidth fall back to item of first element
33778 Roo.log("set column width?");
33779 this.initialColumnWidth = this.columnWidth ;
33781 // if first elem has no width, default to size of container
33786 if (this.initialColumnWidth) {
33787 this.columnWidth = this.initialColumnWidth;
33792 // column width is fixed at the top - however if container width get's smaller we should
33795 // this bit calcs how man columns..
33797 var columnWidth = this.columnWidth += this.gutter;
33799 // calculate columns
33800 var containerWidth = this.containerWidth + this.gutter;
33802 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33803 // fix rounding errors, typically with gutters
33804 var excess = columnWidth - containerWidth % columnWidth;
33807 // if overshoot is less than a pixel, round up, otherwise floor it
33808 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33809 cols = Math[ mathMethod ]( cols );
33810 this.cols = Math.max( cols, 1 );
33811 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33813 // padding positioning..
33814 var totalColWidth = this.cols * this.columnWidth;
33815 var padavail = this.containerWidth - totalColWidth;
33816 // so for 2 columns - we need 3 'pads'
33818 var padNeeded = (1+this.cols) * this.padWidth;
33820 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33822 this.columnWidth += padExtra
33823 //this.padWidth = Math.floor(padavail / ( this.cols));
33825 // adjust colum width so that padding is fixed??
33827 // we have 3 columns ... total = width * 3
33828 // we have X left over... that should be used by
33830 //if (this.expandC) {
33838 getContainerWidth : function()
33840 /* // container is parent if fit width
33841 var container = this.isFitWidth ? this.element.parentNode : this.element;
33842 // check that this.size and size are there
33843 // IE8 triggers resize on body size change, so they might not be
33845 var size = getSize( container ); //FIXME
33846 this.containerWidth = size && size.innerWidth; //FIXME
33849 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33853 _getItemLayoutPosition : function( item ) // what is item?
33855 // we resize the item to our columnWidth..
33857 item.setWidth(this.columnWidth);
33858 item.autoBoxAdjust = false;
33860 var sz = item.getSize();
33862 // how many columns does this brick span
33863 var remainder = this.containerWidth % this.columnWidth;
33865 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33866 // round if off by 1 pixel, otherwise use ceil
33867 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33868 colSpan = Math.min( colSpan, this.cols );
33870 // normally this should be '1' as we dont' currently allow multi width columns..
33872 var colGroup = this._getColGroup( colSpan );
33873 // get the minimum Y value from the columns
33874 var minimumY = Math.min.apply( Math, colGroup );
33875 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33877 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33879 // position the brick
33881 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33882 y: this.currentSize.y + minimumY + this.padHeight
33886 // apply setHeight to necessary columns
33887 var setHeight = minimumY + sz.height + this.padHeight;
33888 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33890 var setSpan = this.cols + 1 - colGroup.length;
33891 for ( var i = 0; i < setSpan; i++ ) {
33892 this.colYs[ shortColIndex + i ] = setHeight ;
33899 * @param {Number} colSpan - number of columns the element spans
33900 * @returns {Array} colGroup
33902 _getColGroup : function( colSpan )
33904 if ( colSpan < 2 ) {
33905 // if brick spans only one column, use all the column Ys
33910 // how many different places could this brick fit horizontally
33911 var groupCount = this.cols + 1 - colSpan;
33912 // for each group potential horizontal position
33913 for ( var i = 0; i < groupCount; i++ ) {
33914 // make an array of colY values for that one group
33915 var groupColYs = this.colYs.slice( i, i + colSpan );
33916 // and get the max value of the array
33917 colGroup[i] = Math.max.apply( Math, groupColYs );
33922 _manageStamp : function( stamp )
33924 var stampSize = stamp.getSize();
33925 var offset = stamp.getBox();
33926 // get the columns that this stamp affects
33927 var firstX = this.isOriginLeft ? offset.x : offset.right;
33928 var lastX = firstX + stampSize.width;
33929 var firstCol = Math.floor( firstX / this.columnWidth );
33930 firstCol = Math.max( 0, firstCol );
33932 var lastCol = Math.floor( lastX / this.columnWidth );
33933 // lastCol should not go over if multiple of columnWidth #425
33934 lastCol -= lastX % this.columnWidth ? 0 : 1;
33935 lastCol = Math.min( this.cols - 1, lastCol );
33937 // set colYs to bottom of the stamp
33938 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33941 for ( var i = firstCol; i <= lastCol; i++ ) {
33942 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33947 _getContainerSize : function()
33949 this.maxY = Math.max.apply( Math, this.colYs );
33954 if ( this.isFitWidth ) {
33955 size.width = this._getContainerFitWidth();
33961 _getContainerFitWidth : function()
33963 var unusedCols = 0;
33964 // count unused columns
33967 if ( this.colYs[i] !== 0 ) {
33972 // fit container to columns that have been used
33973 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33976 needsResizeLayout : function()
33978 var previousWidth = this.containerWidth;
33979 this.getContainerWidth();
33980 return previousWidth !== this.containerWidth;
33995 * @class Roo.bootstrap.MasonryBrick
33996 * @extends Roo.bootstrap.Component
33997 * Bootstrap MasonryBrick class
34000 * Create a new MasonryBrick
34001 * @param {Object} config The config object
34004 Roo.bootstrap.MasonryBrick = function(config){
34006 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34008 Roo.bootstrap.MasonryBrick.register(this);
34014 * When a MasonryBrick is clcik
34015 * @param {Roo.bootstrap.MasonryBrick} this
34016 * @param {Roo.EventObject} e
34022 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34025 * @cfg {String} title
34029 * @cfg {String} html
34033 * @cfg {String} bgimage
34037 * @cfg {String} videourl
34041 * @cfg {String} cls
34045 * @cfg {String} href
34049 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34054 * @cfg {String} placetitle (center|bottom)
34059 * @cfg {Boolean} isFitContainer defalut true
34061 isFitContainer : true,
34064 * @cfg {Boolean} preventDefault defalut false
34066 preventDefault : false,
34069 * @cfg {Boolean} inverse defalut false
34071 maskInverse : false,
34073 getAutoCreate : function()
34075 if(!this.isFitContainer){
34076 return this.getSplitAutoCreate();
34079 var cls = 'masonry-brick masonry-brick-full';
34081 if(this.href.length){
34082 cls += ' masonry-brick-link';
34085 if(this.bgimage.length){
34086 cls += ' masonry-brick-image';
34089 if(this.maskInverse){
34090 cls += ' mask-inverse';
34093 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34094 cls += ' enable-mask';
34098 cls += ' masonry-' + this.size + '-brick';
34101 if(this.placetitle.length){
34103 switch (this.placetitle) {
34105 cls += ' masonry-center-title';
34108 cls += ' masonry-bottom-title';
34115 if(!this.html.length && !this.bgimage.length){
34116 cls += ' masonry-center-title';
34119 if(!this.html.length && this.bgimage.length){
34120 cls += ' masonry-bottom-title';
34125 cls += ' ' + this.cls;
34129 tag: (this.href.length) ? 'a' : 'div',
34134 cls: 'masonry-brick-mask'
34138 cls: 'masonry-brick-paragraph',
34144 if(this.href.length){
34145 cfg.href = this.href;
34148 var cn = cfg.cn[1].cn;
34150 if(this.title.length){
34153 cls: 'masonry-brick-title',
34158 if(this.html.length){
34161 cls: 'masonry-brick-text',
34166 if (!this.title.length && !this.html.length) {
34167 cfg.cn[1].cls += ' hide';
34170 if(this.bgimage.length){
34173 cls: 'masonry-brick-image-view',
34178 if(this.videourl.length){
34179 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34180 // youtube support only?
34183 cls: 'masonry-brick-image-view',
34186 allowfullscreen : true
34194 getSplitAutoCreate : function()
34196 var cls = 'masonry-brick masonry-brick-split';
34198 if(this.href.length){
34199 cls += ' masonry-brick-link';
34202 if(this.bgimage.length){
34203 cls += ' masonry-brick-image';
34207 cls += ' masonry-' + this.size + '-brick';
34210 switch (this.placetitle) {
34212 cls += ' masonry-center-title';
34215 cls += ' masonry-bottom-title';
34218 if(!this.bgimage.length){
34219 cls += ' masonry-center-title';
34222 if(this.bgimage.length){
34223 cls += ' masonry-bottom-title';
34229 cls += ' ' + this.cls;
34233 tag: (this.href.length) ? 'a' : 'div',
34238 cls: 'masonry-brick-split-head',
34242 cls: 'masonry-brick-paragraph',
34249 cls: 'masonry-brick-split-body',
34255 if(this.href.length){
34256 cfg.href = this.href;
34259 if(this.title.length){
34260 cfg.cn[0].cn[0].cn.push({
34262 cls: 'masonry-brick-title',
34267 if(this.html.length){
34268 cfg.cn[1].cn.push({
34270 cls: 'masonry-brick-text',
34275 if(this.bgimage.length){
34276 cfg.cn[0].cn.push({
34278 cls: 'masonry-brick-image-view',
34283 if(this.videourl.length){
34284 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34285 // youtube support only?
34286 cfg.cn[0].cn.cn.push({
34288 cls: 'masonry-brick-image-view',
34291 allowfullscreen : true
34298 initEvents: function()
34300 switch (this.size) {
34333 this.el.on('touchstart', this.onTouchStart, this);
34334 this.el.on('touchmove', this.onTouchMove, this);
34335 this.el.on('touchend', this.onTouchEnd, this);
34336 this.el.on('contextmenu', this.onContextMenu, this);
34338 this.el.on('mouseenter' ,this.enter, this);
34339 this.el.on('mouseleave', this.leave, this);
34340 this.el.on('click', this.onClick, this);
34343 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34344 this.parent().bricks.push(this);
34349 onClick: function(e, el)
34351 var time = this.endTimer - this.startTimer;
34352 // Roo.log(e.preventDefault());
34355 e.preventDefault();
34360 if(!this.preventDefault){
34364 e.preventDefault();
34366 if (this.activeClass != '') {
34367 this.selectBrick();
34370 this.fireEvent('click', this, e);
34373 enter: function(e, el)
34375 e.preventDefault();
34377 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34381 if(this.bgimage.length && this.html.length){
34382 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34386 leave: function(e, el)
34388 e.preventDefault();
34390 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34394 if(this.bgimage.length && this.html.length){
34395 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34399 onTouchStart: function(e, el)
34401 // e.preventDefault();
34403 this.touchmoved = false;
34405 if(!this.isFitContainer){
34409 if(!this.bgimage.length || !this.html.length){
34413 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34415 this.timer = new Date().getTime();
34419 onTouchMove: function(e, el)
34421 this.touchmoved = true;
34424 onContextMenu : function(e,el)
34426 e.preventDefault();
34427 e.stopPropagation();
34431 onTouchEnd: function(e, el)
34433 // e.preventDefault();
34435 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34442 if(!this.bgimage.length || !this.html.length){
34444 if(this.href.length){
34445 window.location.href = this.href;
34451 if(!this.isFitContainer){
34455 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34457 window.location.href = this.href;
34460 //selection on single brick only
34461 selectBrick : function() {
34463 if (!this.parentId) {
34467 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34468 var index = m.selectedBrick.indexOf(this.id);
34471 m.selectedBrick.splice(index,1);
34472 this.el.removeClass(this.activeClass);
34476 for(var i = 0; i < m.selectedBrick.length; i++) {
34477 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34478 b.el.removeClass(b.activeClass);
34481 m.selectedBrick = [];
34483 m.selectedBrick.push(this.id);
34484 this.el.addClass(this.activeClass);
34488 isSelected : function(){
34489 return this.el.hasClass(this.activeClass);
34494 Roo.apply(Roo.bootstrap.MasonryBrick, {
34497 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34499 * register a Masonry Brick
34500 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34503 register : function(brick)
34505 //this.groups[brick.id] = brick;
34506 this.groups.add(brick.id, brick);
34509 * fetch a masonry brick based on the masonry brick ID
34510 * @param {string} the masonry brick to add
34511 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34514 get: function(brick_id)
34516 // if (typeof(this.groups[brick_id]) == 'undefined') {
34519 // return this.groups[brick_id] ;
34521 if(this.groups.key(brick_id)) {
34522 return this.groups.key(brick_id);
34540 * @class Roo.bootstrap.Brick
34541 * @extends Roo.bootstrap.Component
34542 * Bootstrap Brick class
34545 * Create a new Brick
34546 * @param {Object} config The config object
34549 Roo.bootstrap.Brick = function(config){
34550 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34556 * When a Brick is click
34557 * @param {Roo.bootstrap.Brick} this
34558 * @param {Roo.EventObject} e
34564 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
34567 * @cfg {String} title
34571 * @cfg {String} html
34575 * @cfg {String} bgimage
34579 * @cfg {String} cls
34583 * @cfg {String} href
34587 * @cfg {String} video
34591 * @cfg {Boolean} square
34595 getAutoCreate : function()
34597 var cls = 'roo-brick';
34599 if(this.href.length){
34600 cls += ' roo-brick-link';
34603 if(this.bgimage.length){
34604 cls += ' roo-brick-image';
34607 if(!this.html.length && !this.bgimage.length){
34608 cls += ' roo-brick-center-title';
34611 if(!this.html.length && this.bgimage.length){
34612 cls += ' roo-brick-bottom-title';
34616 cls += ' ' + this.cls;
34620 tag: (this.href.length) ? 'a' : 'div',
34625 cls: 'roo-brick-paragraph',
34631 if(this.href.length){
34632 cfg.href = this.href;
34635 var cn = cfg.cn[0].cn;
34637 if(this.title.length){
34640 cls: 'roo-brick-title',
34645 if(this.html.length){
34648 cls: 'roo-brick-text',
34655 if(this.bgimage.length){
34658 cls: 'roo-brick-image-view',
34666 initEvents: function()
34668 if(this.title.length || this.html.length){
34669 this.el.on('mouseenter' ,this.enter, this);
34670 this.el.on('mouseleave', this.leave, this);
34673 Roo.EventManager.onWindowResize(this.resize, this);
34675 if(this.bgimage.length){
34676 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34677 this.imageEl.on('load', this.onImageLoad, this);
34684 onImageLoad : function()
34689 resize : function()
34691 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34693 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34695 if(this.bgimage.length){
34696 var image = this.el.select('.roo-brick-image-view', true).first();
34698 image.setWidth(paragraph.getWidth());
34701 image.setHeight(paragraph.getWidth());
34704 this.el.setHeight(image.getHeight());
34705 paragraph.setHeight(image.getHeight());
34711 enter: function(e, el)
34713 e.preventDefault();
34715 if(this.bgimage.length){
34716 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34717 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34721 leave: function(e, el)
34723 e.preventDefault();
34725 if(this.bgimage.length){
34726 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34727 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34742 * @class Roo.bootstrap.NumberField
34743 * @extends Roo.bootstrap.Input
34744 * Bootstrap NumberField class
34750 * Create a new NumberField
34751 * @param {Object} config The config object
34754 Roo.bootstrap.NumberField = function(config){
34755 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34758 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34761 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34763 allowDecimals : true,
34765 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34767 decimalSeparator : ".",
34769 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34771 decimalPrecision : 2,
34773 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34775 allowNegative : true,
34778 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34782 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34784 minValue : Number.NEGATIVE_INFINITY,
34786 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34788 maxValue : Number.MAX_VALUE,
34790 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34792 minText : "The minimum value for this field is {0}",
34794 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34796 maxText : "The maximum value for this field is {0}",
34798 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34799 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34801 nanText : "{0} is not a valid number",
34803 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34805 thousandsDelimiter : false,
34807 * @cfg {String} valueAlign alignment of value
34809 valueAlign : "left",
34811 getAutoCreate : function()
34813 var hiddenInput = {
34817 cls: 'hidden-number-input'
34821 hiddenInput.name = this.name;
34826 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34828 this.name = hiddenInput.name;
34830 if(cfg.cn.length > 0) {
34831 cfg.cn.push(hiddenInput);
34838 initEvents : function()
34840 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34842 var allowed = "0123456789";
34844 if(this.allowDecimals){
34845 allowed += this.decimalSeparator;
34848 if(this.allowNegative){
34852 if(this.thousandsDelimiter) {
34856 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34858 var keyPress = function(e){
34860 var k = e.getKey();
34862 var c = e.getCharCode();
34865 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34866 allowed.indexOf(String.fromCharCode(c)) === -1
34872 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34876 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34881 this.el.on("keypress", keyPress, this);
34884 validateValue : function(value)
34887 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34891 var num = this.parseValue(value);
34894 this.markInvalid(String.format(this.nanText, value));
34898 if(num < this.minValue){
34899 this.markInvalid(String.format(this.minText, this.minValue));
34903 if(num > this.maxValue){
34904 this.markInvalid(String.format(this.maxText, this.maxValue));
34911 getValue : function()
34913 var v = this.hiddenEl().getValue();
34915 return this.fixPrecision(this.parseValue(v));
34918 parseValue : function(value)
34920 if(this.thousandsDelimiter) {
34922 r = new RegExp(",", "g");
34923 value = value.replace(r, "");
34926 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34927 return isNaN(value) ? '' : value;
34930 fixPrecision : function(value)
34932 if(this.thousandsDelimiter) {
34934 r = new RegExp(",", "g");
34935 value = value.replace(r, "");
34938 var nan = isNaN(value);
34940 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34941 return nan ? '' : value;
34943 return parseFloat(value).toFixed(this.decimalPrecision);
34946 setValue : function(v)
34948 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34954 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34956 this.inputEl().dom.value = (v == '') ? '' :
34957 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34959 if(!this.allowZero && v === '0') {
34960 this.hiddenEl().dom.value = '';
34961 this.inputEl().dom.value = '';
34968 decimalPrecisionFcn : function(v)
34970 return Math.floor(v);
34973 beforeBlur : function()
34975 var v = this.parseValue(this.getRawValue());
34977 if(v || v === 0 || v === ''){
34982 hiddenEl : function()
34984 return this.el.select('input.hidden-number-input',true).first();
34996 * @class Roo.bootstrap.DocumentSlider
34997 * @extends Roo.bootstrap.Component
34998 * Bootstrap DocumentSlider class
35001 * Create a new DocumentViewer
35002 * @param {Object} config The config object
35005 Roo.bootstrap.DocumentSlider = function(config){
35006 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35013 * Fire after initEvent
35014 * @param {Roo.bootstrap.DocumentSlider} this
35019 * Fire after update
35020 * @param {Roo.bootstrap.DocumentSlider} this
35026 * @param {Roo.bootstrap.DocumentSlider} this
35032 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35038 getAutoCreate : function()
35042 cls : 'roo-document-slider',
35046 cls : 'roo-document-slider-header',
35050 cls : 'roo-document-slider-header-title'
35056 cls : 'roo-document-slider-body',
35060 cls : 'roo-document-slider-prev',
35064 cls : 'fa fa-chevron-left'
35070 cls : 'roo-document-slider-thumb',
35074 cls : 'roo-document-slider-image'
35080 cls : 'roo-document-slider-next',
35084 cls : 'fa fa-chevron-right'
35096 initEvents : function()
35098 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35099 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35101 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35102 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35104 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35105 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35107 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35108 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35110 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35111 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35113 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35114 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35116 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35117 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35119 this.thumbEl.on('click', this.onClick, this);
35121 this.prevIndicator.on('click', this.prev, this);
35123 this.nextIndicator.on('click', this.next, this);
35127 initial : function()
35129 if(this.files.length){
35130 this.indicator = 1;
35134 this.fireEvent('initial', this);
35137 update : function()
35139 this.imageEl.attr('src', this.files[this.indicator - 1]);
35141 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35143 this.prevIndicator.show();
35145 if(this.indicator == 1){
35146 this.prevIndicator.hide();
35149 this.nextIndicator.show();
35151 if(this.indicator == this.files.length){
35152 this.nextIndicator.hide();
35155 this.thumbEl.scrollTo('top');
35157 this.fireEvent('update', this);
35160 onClick : function(e)
35162 e.preventDefault();
35164 this.fireEvent('click', this);
35169 e.preventDefault();
35171 this.indicator = Math.max(1, this.indicator - 1);
35178 e.preventDefault();
35180 this.indicator = Math.min(this.files.length, this.indicator + 1);
35194 * @class Roo.bootstrap.RadioSet
35195 * @extends Roo.bootstrap.Input
35196 * Bootstrap RadioSet class
35197 * @cfg {String} indicatorpos (left|right) default left
35198 * @cfg {Boolean} inline (true|false) inline the element (default true)
35199 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35201 * Create a new RadioSet
35202 * @param {Object} config The config object
35205 Roo.bootstrap.RadioSet = function(config){
35207 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35211 Roo.bootstrap.RadioSet.register(this);
35216 * Fires when the element is checked or unchecked.
35217 * @param {Roo.bootstrap.RadioSet} this This radio
35218 * @param {Roo.bootstrap.Radio} item The checked item
35223 * Fires when the element is click.
35224 * @param {Roo.bootstrap.RadioSet} this This radio set
35225 * @param {Roo.bootstrap.Radio} item The checked item
35226 * @param {Roo.EventObject} e The event object
35233 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35241 indicatorpos : 'left',
35243 getAutoCreate : function()
35247 cls : 'roo-radio-set-label',
35251 html : this.fieldLabel
35255 if (Roo.bootstrap.version == 3) {
35258 if(this.indicatorpos == 'left'){
35261 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35262 tooltip : 'This field is required'
35267 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35268 tooltip : 'This field is required'
35274 cls : 'roo-radio-set-items'
35277 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35279 if (align === 'left' && this.fieldLabel.length) {
35282 cls : "roo-radio-set-right",
35288 if(this.labelWidth > 12){
35289 label.style = "width: " + this.labelWidth + 'px';
35292 if(this.labelWidth < 13 && this.labelmd == 0){
35293 this.labelmd = this.labelWidth;
35296 if(this.labellg > 0){
35297 label.cls += ' col-lg-' + this.labellg;
35298 items.cls += ' col-lg-' + (12 - this.labellg);
35301 if(this.labelmd > 0){
35302 label.cls += ' col-md-' + this.labelmd;
35303 items.cls += ' col-md-' + (12 - this.labelmd);
35306 if(this.labelsm > 0){
35307 label.cls += ' col-sm-' + this.labelsm;
35308 items.cls += ' col-sm-' + (12 - this.labelsm);
35311 if(this.labelxs > 0){
35312 label.cls += ' col-xs-' + this.labelxs;
35313 items.cls += ' col-xs-' + (12 - this.labelxs);
35319 cls : 'roo-radio-set',
35323 cls : 'roo-radio-set-input',
35326 value : this.value ? this.value : ''
35333 if(this.weight.length){
35334 cfg.cls += ' roo-radio-' + this.weight;
35338 cfg.cls += ' roo-radio-set-inline';
35342 ['xs','sm','md','lg'].map(function(size){
35343 if (settings[size]) {
35344 cfg.cls += ' col-' + size + '-' + settings[size];
35352 initEvents : function()
35354 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35355 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35357 if(!this.fieldLabel.length){
35358 this.labelEl.hide();
35361 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35362 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35364 this.indicator = this.indicatorEl();
35366 if(this.indicator){
35367 this.indicator.addClass('invisible');
35370 this.originalValue = this.getValue();
35374 inputEl: function ()
35376 return this.el.select('.roo-radio-set-input', true).first();
35379 getChildContainer : function()
35381 return this.itemsEl;
35384 register : function(item)
35386 this.radioes.push(item);
35390 validate : function()
35392 if(this.getVisibilityEl().hasClass('hidden')){
35398 Roo.each(this.radioes, function(i){
35407 if(this.allowBlank) {
35411 if(this.disabled || valid){
35416 this.markInvalid();
35421 markValid : function()
35423 if(this.labelEl.isVisible(true) && this.indicatorEl()){
35424 this.indicatorEl().removeClass('visible');
35425 this.indicatorEl().addClass('invisible');
35429 if (Roo.bootstrap.version == 3) {
35430 this.el.removeClass([this.invalidClass, this.validClass]);
35431 this.el.addClass(this.validClass);
35433 this.el.removeClass(['is-invalid','is-valid']);
35434 this.el.addClass(['is-valid']);
35436 this.fireEvent('valid', this);
35439 markInvalid : function(msg)
35441 if(this.allowBlank || this.disabled){
35445 if(this.labelEl.isVisible(true) && this.indicatorEl()){
35446 this.indicatorEl().removeClass('invisible');
35447 this.indicatorEl().addClass('visible');
35449 if (Roo.bootstrap.version == 3) {
35450 this.el.removeClass([this.invalidClass, this.validClass]);
35451 this.el.addClass(this.invalidClass);
35453 this.el.removeClass(['is-invalid','is-valid']);
35454 this.el.addClass(['is-invalid']);
35457 this.fireEvent('invalid', this, msg);
35461 setValue : function(v, suppressEvent)
35463 if(this.value === v){
35470 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35473 Roo.each(this.radioes, function(i){
35475 i.el.removeClass('checked');
35478 Roo.each(this.radioes, function(i){
35480 if(i.value === v || i.value.toString() === v.toString()){
35482 i.el.addClass('checked');
35484 if(suppressEvent !== true){
35485 this.fireEvent('check', this, i);
35496 clearInvalid : function(){
35498 if(!this.el || this.preventMark){
35502 this.el.removeClass([this.invalidClass]);
35504 this.fireEvent('valid', this);
35509 Roo.apply(Roo.bootstrap.RadioSet, {
35513 register : function(set)
35515 this.groups[set.name] = set;
35518 get: function(name)
35520 if (typeof(this.groups[name]) == 'undefined') {
35524 return this.groups[name] ;
35530 * Ext JS Library 1.1.1
35531 * Copyright(c) 2006-2007, Ext JS, LLC.
35533 * Originally Released Under LGPL - original licence link has changed is not relivant.
35536 * <script type="text/javascript">
35541 * @class Roo.bootstrap.SplitBar
35542 * @extends Roo.util.Observable
35543 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35547 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35548 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35549 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35550 split.minSize = 100;
35551 split.maxSize = 600;
35552 split.animate = true;
35553 split.on('moved', splitterMoved);
35556 * Create a new SplitBar
35557 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
35558 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
35559 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35560 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
35561 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35562 position of the SplitBar).
35564 Roo.bootstrap.SplitBar = function(cfg){
35569 // dragElement : elm
35570 // resizingElement: el,
35572 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35573 // placement : Roo.bootstrap.SplitBar.LEFT ,
35574 // existingProxy ???
35577 this.el = Roo.get(cfg.dragElement, true);
35578 this.el.dom.unselectable = "on";
35580 this.resizingEl = Roo.get(cfg.resizingElement, true);
35584 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35585 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35588 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35591 * The minimum size of the resizing element. (Defaults to 0)
35597 * The maximum size of the resizing element. (Defaults to 2000)
35600 this.maxSize = 2000;
35603 * Whether to animate the transition to the new size
35606 this.animate = false;
35609 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35612 this.useShim = false;
35617 if(!cfg.existingProxy){
35619 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35621 this.proxy = Roo.get(cfg.existingProxy).dom;
35624 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35627 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35630 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35633 this.dragSpecs = {};
35636 * @private The adapter to use to positon and resize elements
35638 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35639 this.adapter.init(this);
35641 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35643 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35644 this.el.addClass("roo-splitbar-h");
35647 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35648 this.el.addClass("roo-splitbar-v");
35654 * Fires when the splitter is moved (alias for {@link #event-moved})
35655 * @param {Roo.bootstrap.SplitBar} this
35656 * @param {Number} newSize the new width or height
35661 * Fires when the splitter is moved
35662 * @param {Roo.bootstrap.SplitBar} this
35663 * @param {Number} newSize the new width or height
35667 * @event beforeresize
35668 * Fires before the splitter is dragged
35669 * @param {Roo.bootstrap.SplitBar} this
35671 "beforeresize" : true,
35673 "beforeapply" : true
35676 Roo.util.Observable.call(this);
35679 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35680 onStartProxyDrag : function(x, y){
35681 this.fireEvent("beforeresize", this);
35683 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35685 o.enableDisplayMode("block");
35686 // all splitbars share the same overlay
35687 Roo.bootstrap.SplitBar.prototype.overlay = o;
35689 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35690 this.overlay.show();
35691 Roo.get(this.proxy).setDisplayed("block");
35692 var size = this.adapter.getElementSize(this);
35693 this.activeMinSize = this.getMinimumSize();;
35694 this.activeMaxSize = this.getMaximumSize();;
35695 var c1 = size - this.activeMinSize;
35696 var c2 = Math.max(this.activeMaxSize - size, 0);
35697 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35698 this.dd.resetConstraints();
35699 this.dd.setXConstraint(
35700 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35701 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35703 this.dd.setYConstraint(0, 0);
35705 this.dd.resetConstraints();
35706 this.dd.setXConstraint(0, 0);
35707 this.dd.setYConstraint(
35708 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35709 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35712 this.dragSpecs.startSize = size;
35713 this.dragSpecs.startPoint = [x, y];
35714 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35718 * @private Called after the drag operation by the DDProxy
35720 onEndProxyDrag : function(e){
35721 Roo.get(this.proxy).setDisplayed(false);
35722 var endPoint = Roo.lib.Event.getXY(e);
35724 this.overlay.hide();
35727 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35728 newSize = this.dragSpecs.startSize +
35729 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35730 endPoint[0] - this.dragSpecs.startPoint[0] :
35731 this.dragSpecs.startPoint[0] - endPoint[0]
35734 newSize = this.dragSpecs.startSize +
35735 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35736 endPoint[1] - this.dragSpecs.startPoint[1] :
35737 this.dragSpecs.startPoint[1] - endPoint[1]
35740 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35741 if(newSize != this.dragSpecs.startSize){
35742 if(this.fireEvent('beforeapply', this, newSize) !== false){
35743 this.adapter.setElementSize(this, newSize);
35744 this.fireEvent("moved", this, newSize);
35745 this.fireEvent("resize", this, newSize);
35751 * Get the adapter this SplitBar uses
35752 * @return The adapter object
35754 getAdapter : function(){
35755 return this.adapter;
35759 * Set the adapter this SplitBar uses
35760 * @param {Object} adapter A SplitBar adapter object
35762 setAdapter : function(adapter){
35763 this.adapter = adapter;
35764 this.adapter.init(this);
35768 * Gets the minimum size for the resizing element
35769 * @return {Number} The minimum size
35771 getMinimumSize : function(){
35772 return this.minSize;
35776 * Sets the minimum size for the resizing element
35777 * @param {Number} minSize The minimum size
35779 setMinimumSize : function(minSize){
35780 this.minSize = minSize;
35784 * Gets the maximum size for the resizing element
35785 * @return {Number} The maximum size
35787 getMaximumSize : function(){
35788 return this.maxSize;
35792 * Sets the maximum size for the resizing element
35793 * @param {Number} maxSize The maximum size
35795 setMaximumSize : function(maxSize){
35796 this.maxSize = maxSize;
35800 * Sets the initialize size for the resizing element
35801 * @param {Number} size The initial size
35803 setCurrentSize : function(size){
35804 var oldAnimate = this.animate;
35805 this.animate = false;
35806 this.adapter.setElementSize(this, size);
35807 this.animate = oldAnimate;
35811 * Destroy this splitbar.
35812 * @param {Boolean} removeEl True to remove the element
35814 destroy : function(removeEl){
35816 this.shim.remove();
35819 this.proxy.parentNode.removeChild(this.proxy);
35827 * @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.
35829 Roo.bootstrap.SplitBar.createProxy = function(dir){
35830 var proxy = new Roo.Element(document.createElement("div"));
35831 proxy.unselectable();
35832 var cls = 'roo-splitbar-proxy';
35833 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35834 document.body.appendChild(proxy.dom);
35839 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35840 * Default Adapter. It assumes the splitter and resizing element are not positioned
35841 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35843 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35846 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35847 // do nothing for now
35848 init : function(s){
35852 * Called before drag operations to get the current size of the resizing element.
35853 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35855 getElementSize : function(s){
35856 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35857 return s.resizingEl.getWidth();
35859 return s.resizingEl.getHeight();
35864 * Called after drag operations to set the size of the resizing element.
35865 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35866 * @param {Number} newSize The new size to set
35867 * @param {Function} onComplete A function to be invoked when resizing is complete
35869 setElementSize : function(s, newSize, onComplete){
35870 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35872 s.resizingEl.setWidth(newSize);
35874 onComplete(s, newSize);
35877 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35882 s.resizingEl.setHeight(newSize);
35884 onComplete(s, newSize);
35887 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35894 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35895 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35896 * Adapter that moves the splitter element to align with the resized sizing element.
35897 * Used with an absolute positioned SplitBar.
35898 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35899 * document.body, make sure you assign an id to the body element.
35901 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35902 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35903 this.container = Roo.get(container);
35906 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35907 init : function(s){
35908 this.basic.init(s);
35911 getElementSize : function(s){
35912 return this.basic.getElementSize(s);
35915 setElementSize : function(s, newSize, onComplete){
35916 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35919 moveSplitter : function(s){
35920 var yes = Roo.bootstrap.SplitBar;
35921 switch(s.placement){
35923 s.el.setX(s.resizingEl.getRight());
35926 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35929 s.el.setY(s.resizingEl.getBottom());
35932 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35939 * Orientation constant - Create a vertical SplitBar
35943 Roo.bootstrap.SplitBar.VERTICAL = 1;
35946 * Orientation constant - Create a horizontal SplitBar
35950 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35953 * Placement constant - The resizing element is to the left of the splitter element
35957 Roo.bootstrap.SplitBar.LEFT = 1;
35960 * Placement constant - The resizing element is to the right of the splitter element
35964 Roo.bootstrap.SplitBar.RIGHT = 2;
35967 * Placement constant - The resizing element is positioned above the splitter element
35971 Roo.bootstrap.SplitBar.TOP = 3;
35974 * Placement constant - The resizing element is positioned under splitter element
35978 Roo.bootstrap.SplitBar.BOTTOM = 4;
35979 Roo.namespace("Roo.bootstrap.layout");/*
35981 * Ext JS Library 1.1.1
35982 * Copyright(c) 2006-2007, Ext JS, LLC.
35984 * Originally Released Under LGPL - original licence link has changed is not relivant.
35987 * <script type="text/javascript">
35991 * @class Roo.bootstrap.layout.Manager
35992 * @extends Roo.bootstrap.Component
35993 * Base class for layout managers.
35995 Roo.bootstrap.layout.Manager = function(config)
35997 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36003 /** false to disable window resize monitoring @type Boolean */
36004 this.monitorWindowResize = true;
36009 * Fires when a layout is performed.
36010 * @param {Roo.LayoutManager} this
36014 * @event regionresized
36015 * Fires when the user resizes a region.
36016 * @param {Roo.LayoutRegion} region The resized region
36017 * @param {Number} newSize The new size (width for east/west, height for north/south)
36019 "regionresized" : true,
36021 * @event regioncollapsed
36022 * Fires when a region is collapsed.
36023 * @param {Roo.LayoutRegion} region The collapsed region
36025 "regioncollapsed" : true,
36027 * @event regionexpanded
36028 * Fires when a region is expanded.
36029 * @param {Roo.LayoutRegion} region The expanded region
36031 "regionexpanded" : true
36033 this.updating = false;
36036 this.el = Roo.get(config.el);
36042 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36047 monitorWindowResize : true,
36053 onRender : function(ct, position)
36056 this.el = Roo.get(ct);
36059 //this.fireEvent('render',this);
36063 initEvents: function()
36067 // ie scrollbar fix
36068 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36069 document.body.scroll = "no";
36070 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36071 this.el.position('relative');
36073 this.id = this.el.id;
36074 this.el.addClass("roo-layout-container");
36075 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36076 if(this.el.dom != document.body ) {
36077 this.el.on('resize', this.layout,this);
36078 this.el.on('show', this.layout,this);
36084 * Returns true if this layout is currently being updated
36085 * @return {Boolean}
36087 isUpdating : function(){
36088 return this.updating;
36092 * Suspend the LayoutManager from doing auto-layouts while
36093 * making multiple add or remove calls
36095 beginUpdate : function(){
36096 this.updating = true;
36100 * Restore auto-layouts and optionally disable the manager from performing a layout
36101 * @param {Boolean} noLayout true to disable a layout update
36103 endUpdate : function(noLayout){
36104 this.updating = false;
36110 layout: function(){
36114 onRegionResized : function(region, newSize){
36115 this.fireEvent("regionresized", region, newSize);
36119 onRegionCollapsed : function(region){
36120 this.fireEvent("regioncollapsed", region);
36123 onRegionExpanded : function(region){
36124 this.fireEvent("regionexpanded", region);
36128 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36129 * performs box-model adjustments.
36130 * @return {Object} The size as an object {width: (the width), height: (the height)}
36132 getViewSize : function()
36135 if(this.el.dom != document.body){
36136 size = this.el.getSize();
36138 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36140 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36141 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36146 * Returns the Element this layout is bound to.
36147 * @return {Roo.Element}
36149 getEl : function(){
36154 * Returns the specified region.
36155 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36156 * @return {Roo.LayoutRegion}
36158 getRegion : function(target){
36159 return this.regions[target.toLowerCase()];
36162 onWindowResize : function(){
36163 if(this.monitorWindowResize){
36170 * Ext JS Library 1.1.1
36171 * Copyright(c) 2006-2007, Ext JS, LLC.
36173 * Originally Released Under LGPL - original licence link has changed is not relivant.
36176 * <script type="text/javascript">
36179 * @class Roo.bootstrap.layout.Border
36180 * @extends Roo.bootstrap.layout.Manager
36181 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36182 * please see: examples/bootstrap/nested.html<br><br>
36184 <b>The container the layout is rendered into can be either the body element or any other element.
36185 If it is not the body element, the container needs to either be an absolute positioned element,
36186 or you will need to add "position:relative" to the css of the container. You will also need to specify
36187 the container size if it is not the body element.</b>
36190 * Create a new Border
36191 * @param {Object} config Configuration options
36193 Roo.bootstrap.layout.Border = function(config){
36194 config = config || {};
36195 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36199 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36200 if(config[region]){
36201 config[region].region = region;
36202 this.addRegion(config[region]);
36208 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36210 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36212 parent : false, // this might point to a 'nest' or a ???
36215 * Creates and adds a new region if it doesn't already exist.
36216 * @param {String} target The target region key (north, south, east, west or center).
36217 * @param {Object} config The regions config object
36218 * @return {BorderLayoutRegion} The new region
36220 addRegion : function(config)
36222 if(!this.regions[config.region]){
36223 var r = this.factory(config);
36224 this.bindRegion(r);
36226 return this.regions[config.region];
36230 bindRegion : function(r){
36231 this.regions[r.config.region] = r;
36233 r.on("visibilitychange", this.layout, this);
36234 r.on("paneladded", this.layout, this);
36235 r.on("panelremoved", this.layout, this);
36236 r.on("invalidated", this.layout, this);
36237 r.on("resized", this.onRegionResized, this);
36238 r.on("collapsed", this.onRegionCollapsed, this);
36239 r.on("expanded", this.onRegionExpanded, this);
36243 * Performs a layout update.
36245 layout : function()
36247 if(this.updating) {
36251 // render all the rebions if they have not been done alreayd?
36252 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36253 if(this.regions[region] && !this.regions[region].bodyEl){
36254 this.regions[region].onRender(this.el)
36258 var size = this.getViewSize();
36259 var w = size.width;
36260 var h = size.height;
36265 //var x = 0, y = 0;
36267 var rs = this.regions;
36268 var north = rs["north"];
36269 var south = rs["south"];
36270 var west = rs["west"];
36271 var east = rs["east"];
36272 var center = rs["center"];
36273 //if(this.hideOnLayout){ // not supported anymore
36274 //c.el.setStyle("display", "none");
36276 if(north && north.isVisible()){
36277 var b = north.getBox();
36278 var m = north.getMargins();
36279 b.width = w - (m.left+m.right);
36282 centerY = b.height + b.y + m.bottom;
36283 centerH -= centerY;
36284 north.updateBox(this.safeBox(b));
36286 if(south && south.isVisible()){
36287 var b = south.getBox();
36288 var m = south.getMargins();
36289 b.width = w - (m.left+m.right);
36291 var totalHeight = (b.height + m.top + m.bottom);
36292 b.y = h - totalHeight + m.top;
36293 centerH -= totalHeight;
36294 south.updateBox(this.safeBox(b));
36296 if(west && west.isVisible()){
36297 var b = west.getBox();
36298 var m = west.getMargins();
36299 b.height = centerH - (m.top+m.bottom);
36301 b.y = centerY + m.top;
36302 var totalWidth = (b.width + m.left + m.right);
36303 centerX += totalWidth;
36304 centerW -= totalWidth;
36305 west.updateBox(this.safeBox(b));
36307 if(east && east.isVisible()){
36308 var b = east.getBox();
36309 var m = east.getMargins();
36310 b.height = centerH - (m.top+m.bottom);
36311 var totalWidth = (b.width + m.left + m.right);
36312 b.x = w - totalWidth + m.left;
36313 b.y = centerY + m.top;
36314 centerW -= totalWidth;
36315 east.updateBox(this.safeBox(b));
36318 var m = center.getMargins();
36320 x: centerX + m.left,
36321 y: centerY + m.top,
36322 width: centerW - (m.left+m.right),
36323 height: centerH - (m.top+m.bottom)
36325 //if(this.hideOnLayout){
36326 //center.el.setStyle("display", "block");
36328 center.updateBox(this.safeBox(centerBox));
36331 this.fireEvent("layout", this);
36335 safeBox : function(box){
36336 box.width = Math.max(0, box.width);
36337 box.height = Math.max(0, box.height);
36342 * Adds a ContentPanel (or subclass) to this layout.
36343 * @param {String} target The target region key (north, south, east, west or center).
36344 * @param {Roo.ContentPanel} panel The panel to add
36345 * @return {Roo.ContentPanel} The added panel
36347 add : function(target, panel){
36349 target = target.toLowerCase();
36350 return this.regions[target].add(panel);
36354 * Remove a ContentPanel (or subclass) to this layout.
36355 * @param {String} target The target region key (north, south, east, west or center).
36356 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36357 * @return {Roo.ContentPanel} The removed panel
36359 remove : function(target, panel){
36360 target = target.toLowerCase();
36361 return this.regions[target].remove(panel);
36365 * Searches all regions for a panel with the specified id
36366 * @param {String} panelId
36367 * @return {Roo.ContentPanel} The panel or null if it wasn't found
36369 findPanel : function(panelId){
36370 var rs = this.regions;
36371 for(var target in rs){
36372 if(typeof rs[target] != "function"){
36373 var p = rs[target].getPanel(panelId);
36383 * Searches all regions for a panel with the specified id and activates (shows) it.
36384 * @param {String/ContentPanel} panelId The panels id or the panel itself
36385 * @return {Roo.ContentPanel} The shown panel or null
36387 showPanel : function(panelId) {
36388 var rs = this.regions;
36389 for(var target in rs){
36390 var r = rs[target];
36391 if(typeof r != "function"){
36392 if(r.hasPanel(panelId)){
36393 return r.showPanel(panelId);
36401 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36402 * @param {Roo.state.Provider} provider (optional) An alternate state provider
36405 restoreState : function(provider){
36407 provider = Roo.state.Manager;
36409 var sm = new Roo.LayoutStateManager();
36410 sm.init(this, provider);
36416 * Adds a xtype elements to the layout.
36420 xtype : 'ContentPanel',
36427 xtype : 'NestedLayoutPanel',
36433 items : [ ... list of content panels or nested layout panels.. ]
36437 * @param {Object} cfg Xtype definition of item to add.
36439 addxtype : function(cfg)
36441 // basically accepts a pannel...
36442 // can accept a layout region..!?!?
36443 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36446 // theory? children can only be panels??
36448 //if (!cfg.xtype.match(/Panel$/)) {
36453 if (typeof(cfg.region) == 'undefined') {
36454 Roo.log("Failed to add Panel, region was not set");
36458 var region = cfg.region;
36464 xitems = cfg.items;
36469 if ( region == 'center') {
36470 Roo.log("Center: " + cfg.title);
36476 case 'Content': // ContentPanel (el, cfg)
36477 case 'Scroll': // ContentPanel (el, cfg)
36479 cfg.autoCreate = cfg.autoCreate || true;
36480 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36482 // var el = this.el.createChild();
36483 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36486 this.add(region, ret);
36490 case 'TreePanel': // our new panel!
36491 cfg.el = this.el.createChild();
36492 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36493 this.add(region, ret);
36498 // create a new Layout (which is a Border Layout...
36500 var clayout = cfg.layout;
36501 clayout.el = this.el.createChild();
36502 clayout.items = clayout.items || [];
36506 // replace this exitems with the clayout ones..
36507 xitems = clayout.items;
36509 // force background off if it's in center...
36510 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36511 cfg.background = false;
36513 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
36516 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36517 //console.log('adding nested layout panel ' + cfg.toSource());
36518 this.add(region, ret);
36519 nb = {}; /// find first...
36524 // needs grid and region
36526 //var el = this.getRegion(region).el.createChild();
36528 *var el = this.el.createChild();
36529 // create the grid first...
36530 cfg.grid.container = el;
36531 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36534 if (region == 'center' && this.active ) {
36535 cfg.background = false;
36538 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36540 this.add(region, ret);
36542 if (cfg.background) {
36543 // render grid on panel activation (if panel background)
36544 ret.on('activate', function(gp) {
36545 if (!gp.grid.rendered) {
36546 // gp.grid.render(el);
36550 // cfg.grid.render(el);
36556 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36557 // it was the old xcomponent building that caused this before.
36558 // espeically if border is the top element in the tree.
36568 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36570 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36571 this.add(region, ret);
36575 throw "Can not add '" + cfg.xtype + "' to Border";
36581 this.beginUpdate();
36585 Roo.each(xitems, function(i) {
36586 region = nb && i.region ? i.region : false;
36588 var add = ret.addxtype(i);
36591 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36592 if (!i.background) {
36593 abn[region] = nb[region] ;
36600 // make the last non-background panel active..
36601 //if (nb) { Roo.log(abn); }
36604 for(var r in abn) {
36605 region = this.getRegion(r);
36607 // tried using nb[r], but it does not work..
36609 region.showPanel(abn[r]);
36620 factory : function(cfg)
36623 var validRegions = Roo.bootstrap.layout.Border.regions;
36625 var target = cfg.region;
36628 var r = Roo.bootstrap.layout;
36632 return new r.North(cfg);
36634 return new r.South(cfg);
36636 return new r.East(cfg);
36638 return new r.West(cfg);
36640 return new r.Center(cfg);
36642 throw 'Layout region "'+target+'" not supported.';
36649 * Ext JS Library 1.1.1
36650 * Copyright(c) 2006-2007, Ext JS, LLC.
36652 * Originally Released Under LGPL - original licence link has changed is not relivant.
36655 * <script type="text/javascript">
36659 * @class Roo.bootstrap.layout.Basic
36660 * @extends Roo.util.Observable
36661 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36662 * and does not have a titlebar, tabs or any other features. All it does is size and position
36663 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36664 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36665 * @cfg {string} region the region that it inhabits..
36666 * @cfg {bool} skipConfig skip config?
36670 Roo.bootstrap.layout.Basic = function(config){
36672 this.mgr = config.mgr;
36674 this.position = config.region;
36676 var skipConfig = config.skipConfig;
36680 * @scope Roo.BasicLayoutRegion
36684 * @event beforeremove
36685 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36686 * @param {Roo.LayoutRegion} this
36687 * @param {Roo.ContentPanel} panel The panel
36688 * @param {Object} e The cancel event object
36690 "beforeremove" : true,
36692 * @event invalidated
36693 * Fires when the layout for this region is changed.
36694 * @param {Roo.LayoutRegion} this
36696 "invalidated" : true,
36698 * @event visibilitychange
36699 * Fires when this region is shown or hidden
36700 * @param {Roo.LayoutRegion} this
36701 * @param {Boolean} visibility true or false
36703 "visibilitychange" : true,
36705 * @event paneladded
36706 * Fires when a panel is added.
36707 * @param {Roo.LayoutRegion} this
36708 * @param {Roo.ContentPanel} panel The panel
36710 "paneladded" : true,
36712 * @event panelremoved
36713 * Fires when a panel is removed.
36714 * @param {Roo.LayoutRegion} this
36715 * @param {Roo.ContentPanel} panel The panel
36717 "panelremoved" : true,
36719 * @event beforecollapse
36720 * Fires when this region before collapse.
36721 * @param {Roo.LayoutRegion} this
36723 "beforecollapse" : true,
36726 * Fires when this region is collapsed.
36727 * @param {Roo.LayoutRegion} this
36729 "collapsed" : true,
36732 * Fires when this region is expanded.
36733 * @param {Roo.LayoutRegion} this
36738 * Fires when this region is slid into view.
36739 * @param {Roo.LayoutRegion} this
36741 "slideshow" : true,
36744 * Fires when this region slides out of view.
36745 * @param {Roo.LayoutRegion} this
36747 "slidehide" : true,
36749 * @event panelactivated
36750 * Fires when a panel is activated.
36751 * @param {Roo.LayoutRegion} this
36752 * @param {Roo.ContentPanel} panel The activated panel
36754 "panelactivated" : true,
36757 * Fires when the user resizes this region.
36758 * @param {Roo.LayoutRegion} this
36759 * @param {Number} newSize The new size (width for east/west, height for north/south)
36763 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36764 this.panels = new Roo.util.MixedCollection();
36765 this.panels.getKey = this.getPanelId.createDelegate(this);
36767 this.activePanel = null;
36768 // ensure listeners are added...
36770 if (config.listeners || config.events) {
36771 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36772 listeners : config.listeners || {},
36773 events : config.events || {}
36777 if(skipConfig !== true){
36778 this.applyConfig(config);
36782 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36784 getPanelId : function(p){
36788 applyConfig : function(config){
36789 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36790 this.config = config;
36795 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36796 * the width, for horizontal (north, south) the height.
36797 * @param {Number} newSize The new width or height
36799 resizeTo : function(newSize){
36800 var el = this.el ? this.el :
36801 (this.activePanel ? this.activePanel.getEl() : null);
36803 switch(this.position){
36806 el.setWidth(newSize);
36807 this.fireEvent("resized", this, newSize);
36811 el.setHeight(newSize);
36812 this.fireEvent("resized", this, newSize);
36818 getBox : function(){
36819 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36822 getMargins : function(){
36823 return this.margins;
36826 updateBox : function(box){
36828 var el = this.activePanel.getEl();
36829 el.dom.style.left = box.x + "px";
36830 el.dom.style.top = box.y + "px";
36831 this.activePanel.setSize(box.width, box.height);
36835 * Returns the container element for this region.
36836 * @return {Roo.Element}
36838 getEl : function(){
36839 return this.activePanel;
36843 * Returns true if this region is currently visible.
36844 * @return {Boolean}
36846 isVisible : function(){
36847 return this.activePanel ? true : false;
36850 setActivePanel : function(panel){
36851 panel = this.getPanel(panel);
36852 if(this.activePanel && this.activePanel != panel){
36853 this.activePanel.setActiveState(false);
36854 this.activePanel.getEl().setLeftTop(-10000,-10000);
36856 this.activePanel = panel;
36857 panel.setActiveState(true);
36859 panel.setSize(this.box.width, this.box.height);
36861 this.fireEvent("panelactivated", this, panel);
36862 this.fireEvent("invalidated");
36866 * Show the specified panel.
36867 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36868 * @return {Roo.ContentPanel} The shown panel or null
36870 showPanel : function(panel){
36871 panel = this.getPanel(panel);
36873 this.setActivePanel(panel);
36879 * Get the active panel for this region.
36880 * @return {Roo.ContentPanel} The active panel or null
36882 getActivePanel : function(){
36883 return this.activePanel;
36887 * Add the passed ContentPanel(s)
36888 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36889 * @return {Roo.ContentPanel} The panel added (if only one was added)
36891 add : function(panel){
36892 if(arguments.length > 1){
36893 for(var i = 0, len = arguments.length; i < len; i++) {
36894 this.add(arguments[i]);
36898 if(this.hasPanel(panel)){
36899 this.showPanel(panel);
36902 var el = panel.getEl();
36903 if(el.dom.parentNode != this.mgr.el.dom){
36904 this.mgr.el.dom.appendChild(el.dom);
36906 if(panel.setRegion){
36907 panel.setRegion(this);
36909 this.panels.add(panel);
36910 el.setStyle("position", "absolute");
36911 if(!panel.background){
36912 this.setActivePanel(panel);
36913 if(this.config.initialSize && this.panels.getCount()==1){
36914 this.resizeTo(this.config.initialSize);
36917 this.fireEvent("paneladded", this, panel);
36922 * Returns true if the panel is in this region.
36923 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36924 * @return {Boolean}
36926 hasPanel : function(panel){
36927 if(typeof panel == "object"){ // must be panel obj
36928 panel = panel.getId();
36930 return this.getPanel(panel) ? true : false;
36934 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36935 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36936 * @param {Boolean} preservePanel Overrides the config preservePanel option
36937 * @return {Roo.ContentPanel} The panel that was removed
36939 remove : function(panel, preservePanel){
36940 panel = this.getPanel(panel);
36945 this.fireEvent("beforeremove", this, panel, e);
36946 if(e.cancel === true){
36949 var panelId = panel.getId();
36950 this.panels.removeKey(panelId);
36955 * Returns the panel specified or null if it's not in this region.
36956 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36957 * @return {Roo.ContentPanel}
36959 getPanel : function(id){
36960 if(typeof id == "object"){ // must be panel obj
36963 return this.panels.get(id);
36967 * Returns this regions position (north/south/east/west/center).
36970 getPosition: function(){
36971 return this.position;
36975 * Ext JS Library 1.1.1
36976 * Copyright(c) 2006-2007, Ext JS, LLC.
36978 * Originally Released Under LGPL - original licence link has changed is not relivant.
36981 * <script type="text/javascript">
36985 * @class Roo.bootstrap.layout.Region
36986 * @extends Roo.bootstrap.layout.Basic
36987 * This class represents a region in a layout manager.
36989 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36990 * @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})
36991 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36992 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36993 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36994 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36995 * @cfg {String} title The title for the region (overrides panel titles)
36996 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36997 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36998 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36999 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37000 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37001 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37002 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37003 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37004 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37005 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37007 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37008 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37009 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37010 * @cfg {Number} width For East/West panels
37011 * @cfg {Number} height For North/South panels
37012 * @cfg {Boolean} split To show the splitter
37013 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37015 * @cfg {string} cls Extra CSS classes to add to region
37017 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37018 * @cfg {string} region the region that it inhabits..
37021 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37022 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37024 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37025 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37026 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37028 Roo.bootstrap.layout.Region = function(config)
37030 this.applyConfig(config);
37032 var mgr = config.mgr;
37033 var pos = config.region;
37034 config.skipConfig = true;
37035 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37038 this.onRender(mgr.el);
37041 this.visible = true;
37042 this.collapsed = false;
37043 this.unrendered_panels = [];
37046 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37048 position: '', // set by wrapper (eg. north/south etc..)
37049 unrendered_panels : null, // unrendered panels.
37051 tabPosition : false,
37053 mgr: false, // points to 'Border'
37056 createBody : function(){
37057 /** This region's body element
37058 * @type Roo.Element */
37059 this.bodyEl = this.el.createChild({
37061 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37065 onRender: function(ctr, pos)
37067 var dh = Roo.DomHelper;
37068 /** This region's container element
37069 * @type Roo.Element */
37070 this.el = dh.append(ctr.dom, {
37072 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37074 /** This region's title element
37075 * @type Roo.Element */
37077 this.titleEl = dh.append(this.el.dom, {
37079 unselectable: "on",
37080 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37082 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37083 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37087 this.titleEl.enableDisplayMode();
37088 /** This region's title text element
37089 * @type HTMLElement */
37090 this.titleTextEl = this.titleEl.dom.firstChild;
37091 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37093 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37094 this.closeBtn.enableDisplayMode();
37095 this.closeBtn.on("click", this.closeClicked, this);
37096 this.closeBtn.hide();
37098 this.createBody(this.config);
37099 if(this.config.hideWhenEmpty){
37101 this.on("paneladded", this.validateVisibility, this);
37102 this.on("panelremoved", this.validateVisibility, this);
37104 if(this.autoScroll){
37105 this.bodyEl.setStyle("overflow", "auto");
37107 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37109 //if(c.titlebar !== false){
37110 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37111 this.titleEl.hide();
37113 this.titleEl.show();
37114 if(this.config.title){
37115 this.titleTextEl.innerHTML = this.config.title;
37119 if(this.config.collapsed){
37120 this.collapse(true);
37122 if(this.config.hidden){
37126 if (this.unrendered_panels && this.unrendered_panels.length) {
37127 for (var i =0;i< this.unrendered_panels.length; i++) {
37128 this.add(this.unrendered_panels[i]);
37130 this.unrendered_panels = null;
37136 applyConfig : function(c)
37139 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37140 var dh = Roo.DomHelper;
37141 if(c.titlebar !== false){
37142 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37143 this.collapseBtn.on("click", this.collapse, this);
37144 this.collapseBtn.enableDisplayMode();
37146 if(c.showPin === true || this.showPin){
37147 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37148 this.stickBtn.enableDisplayMode();
37149 this.stickBtn.on("click", this.expand, this);
37150 this.stickBtn.hide();
37155 /** This region's collapsed element
37156 * @type Roo.Element */
37159 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37160 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37163 if(c.floatable !== false){
37164 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37165 this.collapsedEl.on("click", this.collapseClick, this);
37168 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37169 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37170 id: "message", unselectable: "on", style:{"float":"left"}});
37171 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37173 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37174 this.expandBtn.on("click", this.expand, this);
37178 if(this.collapseBtn){
37179 this.collapseBtn.setVisible(c.collapsible == true);
37182 this.cmargins = c.cmargins || this.cmargins ||
37183 (this.position == "west" || this.position == "east" ?
37184 {top: 0, left: 2, right:2, bottom: 0} :
37185 {top: 2, left: 0, right:0, bottom: 2});
37187 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37190 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37192 this.autoScroll = c.autoScroll || false;
37197 this.duration = c.duration || .30;
37198 this.slideDuration = c.slideDuration || .45;
37203 * Returns true if this region is currently visible.
37204 * @return {Boolean}
37206 isVisible : function(){
37207 return this.visible;
37211 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37212 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37214 //setCollapsedTitle : function(title){
37215 // title = title || " ";
37216 // if(this.collapsedTitleTextEl){
37217 // this.collapsedTitleTextEl.innerHTML = title;
37221 getBox : function(){
37223 // if(!this.collapsed){
37224 b = this.el.getBox(false, true);
37226 // b = this.collapsedEl.getBox(false, true);
37231 getMargins : function(){
37232 return this.margins;
37233 //return this.collapsed ? this.cmargins : this.margins;
37236 highlight : function(){
37237 this.el.addClass("x-layout-panel-dragover");
37240 unhighlight : function(){
37241 this.el.removeClass("x-layout-panel-dragover");
37244 updateBox : function(box)
37246 if (!this.bodyEl) {
37247 return; // not rendered yet..
37251 if(!this.collapsed){
37252 this.el.dom.style.left = box.x + "px";
37253 this.el.dom.style.top = box.y + "px";
37254 this.updateBody(box.width, box.height);
37256 this.collapsedEl.dom.style.left = box.x + "px";
37257 this.collapsedEl.dom.style.top = box.y + "px";
37258 this.collapsedEl.setSize(box.width, box.height);
37261 this.tabs.autoSizeTabs();
37265 updateBody : function(w, h)
37268 this.el.setWidth(w);
37269 w -= this.el.getBorderWidth("rl");
37270 if(this.config.adjustments){
37271 w += this.config.adjustments[0];
37274 if(h !== null && h > 0){
37275 this.el.setHeight(h);
37276 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37277 h -= this.el.getBorderWidth("tb");
37278 if(this.config.adjustments){
37279 h += this.config.adjustments[1];
37281 this.bodyEl.setHeight(h);
37283 h = this.tabs.syncHeight(h);
37286 if(this.panelSize){
37287 w = w !== null ? w : this.panelSize.width;
37288 h = h !== null ? h : this.panelSize.height;
37290 if(this.activePanel){
37291 var el = this.activePanel.getEl();
37292 w = w !== null ? w : el.getWidth();
37293 h = h !== null ? h : el.getHeight();
37294 this.panelSize = {width: w, height: h};
37295 this.activePanel.setSize(w, h);
37297 if(Roo.isIE && this.tabs){
37298 this.tabs.el.repaint();
37303 * Returns the container element for this region.
37304 * @return {Roo.Element}
37306 getEl : function(){
37311 * Hides this region.
37314 //if(!this.collapsed){
37315 this.el.dom.style.left = "-2000px";
37318 // this.collapsedEl.dom.style.left = "-2000px";
37319 // this.collapsedEl.hide();
37321 this.visible = false;
37322 this.fireEvent("visibilitychange", this, false);
37326 * Shows this region if it was previously hidden.
37329 //if(!this.collapsed){
37332 // this.collapsedEl.show();
37334 this.visible = true;
37335 this.fireEvent("visibilitychange", this, true);
37338 closeClicked : function(){
37339 if(this.activePanel){
37340 this.remove(this.activePanel);
37344 collapseClick : function(e){
37346 e.stopPropagation();
37349 e.stopPropagation();
37355 * Collapses this region.
37356 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37359 collapse : function(skipAnim, skipCheck = false){
37360 if(this.collapsed) {
37364 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37366 this.collapsed = true;
37368 this.split.el.hide();
37370 if(this.config.animate && skipAnim !== true){
37371 this.fireEvent("invalidated", this);
37372 this.animateCollapse();
37374 this.el.setLocation(-20000,-20000);
37376 this.collapsedEl.show();
37377 this.fireEvent("collapsed", this);
37378 this.fireEvent("invalidated", this);
37384 animateCollapse : function(){
37389 * Expands this region if it was previously collapsed.
37390 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37391 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37394 expand : function(e, skipAnim){
37396 e.stopPropagation();
37398 if(!this.collapsed || this.el.hasActiveFx()) {
37402 this.afterSlideIn();
37405 this.collapsed = false;
37406 if(this.config.animate && skipAnim !== true){
37407 this.animateExpand();
37411 this.split.el.show();
37413 this.collapsedEl.setLocation(-2000,-2000);
37414 this.collapsedEl.hide();
37415 this.fireEvent("invalidated", this);
37416 this.fireEvent("expanded", this);
37420 animateExpand : function(){
37424 initTabs : function()
37426 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37428 var ts = new Roo.bootstrap.panel.Tabs({
37429 el: this.bodyEl.dom,
37431 tabPosition: this.tabPosition ? this.tabPosition : 'top',
37432 disableTooltips: this.config.disableTabTips,
37433 toolbar : this.config.toolbar
37436 if(this.config.hideTabs){
37437 ts.stripWrap.setDisplayed(false);
37440 ts.resizeTabs = this.config.resizeTabs === true;
37441 ts.minTabWidth = this.config.minTabWidth || 40;
37442 ts.maxTabWidth = this.config.maxTabWidth || 250;
37443 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37444 ts.monitorResize = false;
37445 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37446 ts.bodyEl.addClass('roo-layout-tabs-body');
37447 this.panels.each(this.initPanelAsTab, this);
37450 initPanelAsTab : function(panel){
37451 var ti = this.tabs.addTab(
37455 this.config.closeOnTab && panel.isClosable(),
37458 if(panel.tabTip !== undefined){
37459 ti.setTooltip(panel.tabTip);
37461 ti.on("activate", function(){
37462 this.setActivePanel(panel);
37465 if(this.config.closeOnTab){
37466 ti.on("beforeclose", function(t, e){
37468 this.remove(panel);
37472 panel.tabItem = ti;
37477 updatePanelTitle : function(panel, title)
37479 if(this.activePanel == panel){
37480 this.updateTitle(title);
37483 var ti = this.tabs.getTab(panel.getEl().id);
37485 if(panel.tabTip !== undefined){
37486 ti.setTooltip(panel.tabTip);
37491 updateTitle : function(title){
37492 if(this.titleTextEl && !this.config.title){
37493 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
37497 setActivePanel : function(panel)
37499 panel = this.getPanel(panel);
37500 if(this.activePanel && this.activePanel != panel){
37501 if(this.activePanel.setActiveState(false) === false){
37505 this.activePanel = panel;
37506 panel.setActiveState(true);
37507 if(this.panelSize){
37508 panel.setSize(this.panelSize.width, this.panelSize.height);
37511 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37513 this.updateTitle(panel.getTitle());
37515 this.fireEvent("invalidated", this);
37517 this.fireEvent("panelactivated", this, panel);
37521 * Shows the specified panel.
37522 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37523 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37525 showPanel : function(panel)
37527 panel = this.getPanel(panel);
37530 var tab = this.tabs.getTab(panel.getEl().id);
37531 if(tab.isHidden()){
37532 this.tabs.unhideTab(tab.id);
37536 this.setActivePanel(panel);
37543 * Get the active panel for this region.
37544 * @return {Roo.ContentPanel} The active panel or null
37546 getActivePanel : function(){
37547 return this.activePanel;
37550 validateVisibility : function(){
37551 if(this.panels.getCount() < 1){
37552 this.updateTitle(" ");
37553 this.closeBtn.hide();
37556 if(!this.isVisible()){
37563 * Adds the passed ContentPanel(s) to this region.
37564 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37565 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37567 add : function(panel)
37569 if(arguments.length > 1){
37570 for(var i = 0, len = arguments.length; i < len; i++) {
37571 this.add(arguments[i]);
37576 // if we have not been rendered yet, then we can not really do much of this..
37577 if (!this.bodyEl) {
37578 this.unrendered_panels.push(panel);
37585 if(this.hasPanel(panel)){
37586 this.showPanel(panel);
37589 panel.setRegion(this);
37590 this.panels.add(panel);
37591 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37592 // sinle panel - no tab...?? would it not be better to render it with the tabs,
37593 // and hide them... ???
37594 this.bodyEl.dom.appendChild(panel.getEl().dom);
37595 if(panel.background !== true){
37596 this.setActivePanel(panel);
37598 this.fireEvent("paneladded", this, panel);
37605 this.initPanelAsTab(panel);
37609 if(panel.background !== true){
37610 this.tabs.activate(panel.getEl().id);
37612 this.fireEvent("paneladded", this, panel);
37617 * Hides the tab for the specified panel.
37618 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37620 hidePanel : function(panel){
37621 if(this.tabs && (panel = this.getPanel(panel))){
37622 this.tabs.hideTab(panel.getEl().id);
37627 * Unhides the tab for a previously hidden panel.
37628 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37630 unhidePanel : function(panel){
37631 if(this.tabs && (panel = this.getPanel(panel))){
37632 this.tabs.unhideTab(panel.getEl().id);
37636 clearPanels : function(){
37637 while(this.panels.getCount() > 0){
37638 this.remove(this.panels.first());
37643 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37644 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37645 * @param {Boolean} preservePanel Overrides the config preservePanel option
37646 * @return {Roo.ContentPanel} The panel that was removed
37648 remove : function(panel, preservePanel)
37650 panel = this.getPanel(panel);
37655 this.fireEvent("beforeremove", this, panel, e);
37656 if(e.cancel === true){
37659 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37660 var panelId = panel.getId();
37661 this.panels.removeKey(panelId);
37663 document.body.appendChild(panel.getEl().dom);
37666 this.tabs.removeTab(panel.getEl().id);
37667 }else if (!preservePanel){
37668 this.bodyEl.dom.removeChild(panel.getEl().dom);
37670 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37671 var p = this.panels.first();
37672 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37673 tempEl.appendChild(p.getEl().dom);
37674 this.bodyEl.update("");
37675 this.bodyEl.dom.appendChild(p.getEl().dom);
37677 this.updateTitle(p.getTitle());
37679 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37680 this.setActivePanel(p);
37682 panel.setRegion(null);
37683 if(this.activePanel == panel){
37684 this.activePanel = null;
37686 if(this.config.autoDestroy !== false && preservePanel !== true){
37687 try{panel.destroy();}catch(e){}
37689 this.fireEvent("panelremoved", this, panel);
37694 * Returns the TabPanel component used by this region
37695 * @return {Roo.TabPanel}
37697 getTabs : function(){
37701 createTool : function(parentEl, className){
37702 var btn = Roo.DomHelper.append(parentEl, {
37704 cls: "x-layout-tools-button",
37707 cls: "roo-layout-tools-button-inner " + className,
37711 btn.addClassOnOver("roo-layout-tools-button-over");
37716 * Ext JS Library 1.1.1
37717 * Copyright(c) 2006-2007, Ext JS, LLC.
37719 * Originally Released Under LGPL - original licence link has changed is not relivant.
37722 * <script type="text/javascript">
37728 * @class Roo.SplitLayoutRegion
37729 * @extends Roo.LayoutRegion
37730 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37732 Roo.bootstrap.layout.Split = function(config){
37733 this.cursor = config.cursor;
37734 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37737 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37739 splitTip : "Drag to resize.",
37740 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37741 useSplitTips : false,
37743 applyConfig : function(config){
37744 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37747 onRender : function(ctr,pos) {
37749 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37750 if(!this.config.split){
37755 var splitEl = Roo.DomHelper.append(ctr.dom, {
37757 id: this.el.id + "-split",
37758 cls: "roo-layout-split roo-layout-split-"+this.position,
37761 /** The SplitBar for this region
37762 * @type Roo.SplitBar */
37763 // does not exist yet...
37764 Roo.log([this.position, this.orientation]);
37766 this.split = new Roo.bootstrap.SplitBar({
37767 dragElement : splitEl,
37768 resizingElement: this.el,
37769 orientation : this.orientation
37772 this.split.on("moved", this.onSplitMove, this);
37773 this.split.useShim = this.config.useShim === true;
37774 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37775 if(this.useSplitTips){
37776 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37778 //if(config.collapsible){
37779 // this.split.el.on("dblclick", this.collapse, this);
37782 if(typeof this.config.minSize != "undefined"){
37783 this.split.minSize = this.config.minSize;
37785 if(typeof this.config.maxSize != "undefined"){
37786 this.split.maxSize = this.config.maxSize;
37788 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37789 this.hideSplitter();
37794 getHMaxSize : function(){
37795 var cmax = this.config.maxSize || 10000;
37796 var center = this.mgr.getRegion("center");
37797 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37800 getVMaxSize : function(){
37801 var cmax = this.config.maxSize || 10000;
37802 var center = this.mgr.getRegion("center");
37803 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37806 onSplitMove : function(split, newSize){
37807 this.fireEvent("resized", this, newSize);
37811 * Returns the {@link Roo.SplitBar} for this region.
37812 * @return {Roo.SplitBar}
37814 getSplitBar : function(){
37819 this.hideSplitter();
37820 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37823 hideSplitter : function(){
37825 this.split.el.setLocation(-2000,-2000);
37826 this.split.el.hide();
37832 this.split.el.show();
37834 Roo.bootstrap.layout.Split.superclass.show.call(this);
37837 beforeSlide: function(){
37838 if(Roo.isGecko){// firefox overflow auto bug workaround
37839 this.bodyEl.clip();
37841 this.tabs.bodyEl.clip();
37843 if(this.activePanel){
37844 this.activePanel.getEl().clip();
37846 if(this.activePanel.beforeSlide){
37847 this.activePanel.beforeSlide();
37853 afterSlide : function(){
37854 if(Roo.isGecko){// firefox overflow auto bug workaround
37855 this.bodyEl.unclip();
37857 this.tabs.bodyEl.unclip();
37859 if(this.activePanel){
37860 this.activePanel.getEl().unclip();
37861 if(this.activePanel.afterSlide){
37862 this.activePanel.afterSlide();
37868 initAutoHide : function(){
37869 if(this.autoHide !== false){
37870 if(!this.autoHideHd){
37871 var st = new Roo.util.DelayedTask(this.slideIn, this);
37872 this.autoHideHd = {
37873 "mouseout": function(e){
37874 if(!e.within(this.el, true)){
37878 "mouseover" : function(e){
37884 this.el.on(this.autoHideHd);
37888 clearAutoHide : function(){
37889 if(this.autoHide !== false){
37890 this.el.un("mouseout", this.autoHideHd.mouseout);
37891 this.el.un("mouseover", this.autoHideHd.mouseover);
37895 clearMonitor : function(){
37896 Roo.get(document).un("click", this.slideInIf, this);
37899 // these names are backwards but not changed for compat
37900 slideOut : function(){
37901 if(this.isSlid || this.el.hasActiveFx()){
37904 this.isSlid = true;
37905 if(this.collapseBtn){
37906 this.collapseBtn.hide();
37908 this.closeBtnState = this.closeBtn.getStyle('display');
37909 this.closeBtn.hide();
37911 this.stickBtn.show();
37914 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37915 this.beforeSlide();
37916 this.el.setStyle("z-index", 10001);
37917 this.el.slideIn(this.getSlideAnchor(), {
37918 callback: function(){
37920 this.initAutoHide();
37921 Roo.get(document).on("click", this.slideInIf, this);
37922 this.fireEvent("slideshow", this);
37929 afterSlideIn : function(){
37930 this.clearAutoHide();
37931 this.isSlid = false;
37932 this.clearMonitor();
37933 this.el.setStyle("z-index", "");
37934 if(this.collapseBtn){
37935 this.collapseBtn.show();
37937 this.closeBtn.setStyle('display', this.closeBtnState);
37939 this.stickBtn.hide();
37941 this.fireEvent("slidehide", this);
37944 slideIn : function(cb){
37945 if(!this.isSlid || this.el.hasActiveFx()){
37949 this.isSlid = false;
37950 this.beforeSlide();
37951 this.el.slideOut(this.getSlideAnchor(), {
37952 callback: function(){
37953 this.el.setLeftTop(-10000, -10000);
37955 this.afterSlideIn();
37963 slideInIf : function(e){
37964 if(!e.within(this.el)){
37969 animateCollapse : function(){
37970 this.beforeSlide();
37971 this.el.setStyle("z-index", 20000);
37972 var anchor = this.getSlideAnchor();
37973 this.el.slideOut(anchor, {
37974 callback : function(){
37975 this.el.setStyle("z-index", "");
37976 this.collapsedEl.slideIn(anchor, {duration:.3});
37978 this.el.setLocation(-10000,-10000);
37980 this.fireEvent("collapsed", this);
37987 animateExpand : function(){
37988 this.beforeSlide();
37989 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37990 this.el.setStyle("z-index", 20000);
37991 this.collapsedEl.hide({
37994 this.el.slideIn(this.getSlideAnchor(), {
37995 callback : function(){
37996 this.el.setStyle("z-index", "");
37999 this.split.el.show();
38001 this.fireEvent("invalidated", this);
38002 this.fireEvent("expanded", this);
38030 getAnchor : function(){
38031 return this.anchors[this.position];
38034 getCollapseAnchor : function(){
38035 return this.canchors[this.position];
38038 getSlideAnchor : function(){
38039 return this.sanchors[this.position];
38042 getAlignAdj : function(){
38043 var cm = this.cmargins;
38044 switch(this.position){
38060 getExpandAdj : function(){
38061 var c = this.collapsedEl, cm = this.cmargins;
38062 switch(this.position){
38064 return [-(cm.right+c.getWidth()+cm.left), 0];
38067 return [cm.right+c.getWidth()+cm.left, 0];
38070 return [0, -(cm.top+cm.bottom+c.getHeight())];
38073 return [0, cm.top+cm.bottom+c.getHeight()];
38079 * Ext JS Library 1.1.1
38080 * Copyright(c) 2006-2007, Ext JS, LLC.
38082 * Originally Released Under LGPL - original licence link has changed is not relivant.
38085 * <script type="text/javascript">
38088 * These classes are private internal classes
38090 Roo.bootstrap.layout.Center = function(config){
38091 config.region = "center";
38092 Roo.bootstrap.layout.Region.call(this, config);
38093 this.visible = true;
38094 this.minWidth = config.minWidth || 20;
38095 this.minHeight = config.minHeight || 20;
38098 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38100 // center panel can't be hidden
38104 // center panel can't be hidden
38107 getMinWidth: function(){
38108 return this.minWidth;
38111 getMinHeight: function(){
38112 return this.minHeight;
38126 Roo.bootstrap.layout.North = function(config)
38128 config.region = 'north';
38129 config.cursor = 'n-resize';
38131 Roo.bootstrap.layout.Split.call(this, config);
38135 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38136 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38137 this.split.el.addClass("roo-layout-split-v");
38139 var size = config.initialSize || config.height;
38140 if(typeof size != "undefined"){
38141 this.el.setHeight(size);
38144 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38146 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38150 getBox : function(){
38151 if(this.collapsed){
38152 return this.collapsedEl.getBox();
38154 var box = this.el.getBox();
38156 box.height += this.split.el.getHeight();
38161 updateBox : function(box){
38162 if(this.split && !this.collapsed){
38163 box.height -= this.split.el.getHeight();
38164 this.split.el.setLeft(box.x);
38165 this.split.el.setTop(box.y+box.height);
38166 this.split.el.setWidth(box.width);
38168 if(this.collapsed){
38169 this.updateBody(box.width, null);
38171 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38179 Roo.bootstrap.layout.South = function(config){
38180 config.region = 'south';
38181 config.cursor = 's-resize';
38182 Roo.bootstrap.layout.Split.call(this, config);
38184 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38185 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38186 this.split.el.addClass("roo-layout-split-v");
38188 var size = config.initialSize || config.height;
38189 if(typeof size != "undefined"){
38190 this.el.setHeight(size);
38194 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38195 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38196 getBox : function(){
38197 if(this.collapsed){
38198 return this.collapsedEl.getBox();
38200 var box = this.el.getBox();
38202 var sh = this.split.el.getHeight();
38209 updateBox : function(box){
38210 if(this.split && !this.collapsed){
38211 var sh = this.split.el.getHeight();
38214 this.split.el.setLeft(box.x);
38215 this.split.el.setTop(box.y-sh);
38216 this.split.el.setWidth(box.width);
38218 if(this.collapsed){
38219 this.updateBody(box.width, null);
38221 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38225 Roo.bootstrap.layout.East = function(config){
38226 config.region = "east";
38227 config.cursor = "e-resize";
38228 Roo.bootstrap.layout.Split.call(this, config);
38230 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38231 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38232 this.split.el.addClass("roo-layout-split-h");
38234 var size = config.initialSize || config.width;
38235 if(typeof size != "undefined"){
38236 this.el.setWidth(size);
38239 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38240 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38241 getBox : function(){
38242 if(this.collapsed){
38243 return this.collapsedEl.getBox();
38245 var box = this.el.getBox();
38247 var sw = this.split.el.getWidth();
38254 updateBox : function(box){
38255 if(this.split && !this.collapsed){
38256 var sw = this.split.el.getWidth();
38258 this.split.el.setLeft(box.x);
38259 this.split.el.setTop(box.y);
38260 this.split.el.setHeight(box.height);
38263 if(this.collapsed){
38264 this.updateBody(null, box.height);
38266 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38270 Roo.bootstrap.layout.West = function(config){
38271 config.region = "west";
38272 config.cursor = "w-resize";
38274 Roo.bootstrap.layout.Split.call(this, config);
38276 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38277 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38278 this.split.el.addClass("roo-layout-split-h");
38282 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38283 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38285 onRender: function(ctr, pos)
38287 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38288 var size = this.config.initialSize || this.config.width;
38289 if(typeof size != "undefined"){
38290 this.el.setWidth(size);
38294 getBox : function(){
38295 if(this.collapsed){
38296 return this.collapsedEl.getBox();
38298 var box = this.el.getBox();
38300 box.width += this.split.el.getWidth();
38305 updateBox : function(box){
38306 if(this.split && !this.collapsed){
38307 var sw = this.split.el.getWidth();
38309 this.split.el.setLeft(box.x+box.width);
38310 this.split.el.setTop(box.y);
38311 this.split.el.setHeight(box.height);
38313 if(this.collapsed){
38314 this.updateBody(null, box.height);
38316 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38318 });Roo.namespace("Roo.bootstrap.panel");/*
38320 * Ext JS Library 1.1.1
38321 * Copyright(c) 2006-2007, Ext JS, LLC.
38323 * Originally Released Under LGPL - original licence link has changed is not relivant.
38326 * <script type="text/javascript">
38329 * @class Roo.ContentPanel
38330 * @extends Roo.util.Observable
38331 * A basic ContentPanel element.
38332 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38333 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38334 * @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
38335 * @cfg {Boolean} closable True if the panel can be closed/removed
38336 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38337 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38338 * @cfg {Toolbar} toolbar A toolbar for this panel
38339 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38340 * @cfg {String} title The title for this panel
38341 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38342 * @cfg {String} url Calls {@link #setUrl} with this value
38343 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38344 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38345 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38346 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38347 * @cfg {Boolean} badges render the badges
38350 * Create a new ContentPanel.
38351 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38352 * @param {String/Object} config A string to set only the title or a config object
38353 * @param {String} content (optional) Set the HTML content for this panel
38354 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38356 Roo.bootstrap.panel.Content = function( config){
38358 this.tpl = config.tpl || false;
38360 var el = config.el;
38361 var content = config.content;
38363 if(config.autoCreate){ // xtype is available if this is called from factory
38366 this.el = Roo.get(el);
38367 if(!this.el && config && config.autoCreate){
38368 if(typeof config.autoCreate == "object"){
38369 if(!config.autoCreate.id){
38370 config.autoCreate.id = config.id||el;
38372 this.el = Roo.DomHelper.append(document.body,
38373 config.autoCreate, true);
38375 var elcfg = { tag: "div",
38376 cls: "roo-layout-inactive-content",
38380 elcfg.html = config.html;
38384 this.el = Roo.DomHelper.append(document.body, elcfg , true);
38387 this.closable = false;
38388 this.loaded = false;
38389 this.active = false;
38392 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38394 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38396 this.wrapEl = this.el; //this.el.wrap();
38398 if (config.toolbar.items) {
38399 ti = config.toolbar.items ;
38400 delete config.toolbar.items ;
38404 this.toolbar.render(this.wrapEl, 'before');
38405 for(var i =0;i < ti.length;i++) {
38406 // Roo.log(['add child', items[i]]);
38407 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38409 this.toolbar.items = nitems;
38410 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38411 delete config.toolbar;
38415 // xtype created footer. - not sure if will work as we normally have to render first..
38416 if (this.footer && !this.footer.el && this.footer.xtype) {
38417 if (!this.wrapEl) {
38418 this.wrapEl = this.el.wrap();
38421 this.footer.container = this.wrapEl.createChild();
38423 this.footer = Roo.factory(this.footer, Roo);
38428 if(typeof config == "string"){
38429 this.title = config;
38431 Roo.apply(this, config);
38435 this.resizeEl = Roo.get(this.resizeEl, true);
38437 this.resizeEl = this.el;
38439 // handle view.xtype
38447 * Fires when this panel is activated.
38448 * @param {Roo.ContentPanel} this
38452 * @event deactivate
38453 * Fires when this panel is activated.
38454 * @param {Roo.ContentPanel} this
38456 "deactivate" : true,
38460 * Fires when this panel is resized if fitToFrame is true.
38461 * @param {Roo.ContentPanel} this
38462 * @param {Number} width The width after any component adjustments
38463 * @param {Number} height The height after any component adjustments
38469 * Fires when this tab is created
38470 * @param {Roo.ContentPanel} this
38481 if(this.autoScroll){
38482 this.resizeEl.setStyle("overflow", "auto");
38484 // fix randome scrolling
38485 //this.el.on('scroll', function() {
38486 // Roo.log('fix random scolling');
38487 // this.scrollTo('top',0);
38490 content = content || this.content;
38492 this.setContent(content);
38494 if(config && config.url){
38495 this.setUrl(this.url, this.params, this.loadOnce);
38500 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38502 if (this.view && typeof(this.view.xtype) != 'undefined') {
38503 this.view.el = this.el.appendChild(document.createElement("div"));
38504 this.view = Roo.factory(this.view);
38505 this.view.render && this.view.render(false, '');
38509 this.fireEvent('render', this);
38512 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38516 setRegion : function(region){
38517 this.region = region;
38518 this.setActiveClass(region && !this.background);
38522 setActiveClass: function(state)
38525 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38526 this.el.setStyle('position','relative');
38528 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38529 this.el.setStyle('position', 'absolute');
38534 * Returns the toolbar for this Panel if one was configured.
38535 * @return {Roo.Toolbar}
38537 getToolbar : function(){
38538 return this.toolbar;
38541 setActiveState : function(active)
38543 this.active = active;
38544 this.setActiveClass(active);
38546 if(this.fireEvent("deactivate", this) === false){
38551 this.fireEvent("activate", this);
38555 * Updates this panel's element
38556 * @param {String} content The new content
38557 * @param {Boolean} loadScripts (optional) true to look for and process scripts
38559 setContent : function(content, loadScripts){
38560 this.el.update(content, loadScripts);
38563 ignoreResize : function(w, h){
38564 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38567 this.lastSize = {width: w, height: h};
38572 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38573 * @return {Roo.UpdateManager} The UpdateManager
38575 getUpdateManager : function(){
38576 return this.el.getUpdateManager();
38579 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38580 * @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:
38583 url: "your-url.php",
38584 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38585 callback: yourFunction,
38586 scope: yourObject, //(optional scope)
38589 text: "Loading...",
38594 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38595 * 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.
38596 * @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}
38597 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38598 * @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.
38599 * @return {Roo.ContentPanel} this
38602 var um = this.el.getUpdateManager();
38603 um.update.apply(um, arguments);
38609 * 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.
38610 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38611 * @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)
38612 * @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)
38613 * @return {Roo.UpdateManager} The UpdateManager
38615 setUrl : function(url, params, loadOnce){
38616 if(this.refreshDelegate){
38617 this.removeListener("activate", this.refreshDelegate);
38619 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38620 this.on("activate", this.refreshDelegate);
38621 return this.el.getUpdateManager();
38624 _handleRefresh : function(url, params, loadOnce){
38625 if(!loadOnce || !this.loaded){
38626 var updater = this.el.getUpdateManager();
38627 updater.update(url, params, this._setLoaded.createDelegate(this));
38631 _setLoaded : function(){
38632 this.loaded = true;
38636 * Returns this panel's id
38639 getId : function(){
38644 * Returns this panel's element - used by regiosn to add.
38645 * @return {Roo.Element}
38647 getEl : function(){
38648 return this.wrapEl || this.el;
38653 adjustForComponents : function(width, height)
38655 //Roo.log('adjustForComponents ');
38656 if(this.resizeEl != this.el){
38657 width -= this.el.getFrameWidth('lr');
38658 height -= this.el.getFrameWidth('tb');
38661 var te = this.toolbar.getEl();
38662 te.setWidth(width);
38663 height -= te.getHeight();
38666 var te = this.footer.getEl();
38667 te.setWidth(width);
38668 height -= te.getHeight();
38672 if(this.adjustments){
38673 width += this.adjustments[0];
38674 height += this.adjustments[1];
38676 return {"width": width, "height": height};
38679 setSize : function(width, height){
38680 if(this.fitToFrame && !this.ignoreResize(width, height)){
38681 if(this.fitContainer && this.resizeEl != this.el){
38682 this.el.setSize(width, height);
38684 var size = this.adjustForComponents(width, height);
38685 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38686 this.fireEvent('resize', this, size.width, size.height);
38691 * Returns this panel's title
38694 getTitle : function(){
38696 if (typeof(this.title) != 'object') {
38701 for (var k in this.title) {
38702 if (!this.title.hasOwnProperty(k)) {
38706 if (k.indexOf('-') >= 0) {
38707 var s = k.split('-');
38708 for (var i = 0; i<s.length; i++) {
38709 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38712 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38719 * Set this panel's title
38720 * @param {String} title
38722 setTitle : function(title){
38723 this.title = title;
38725 this.region.updatePanelTitle(this, title);
38730 * Returns true is this panel was configured to be closable
38731 * @return {Boolean}
38733 isClosable : function(){
38734 return this.closable;
38737 beforeSlide : function(){
38739 this.resizeEl.clip();
38742 afterSlide : function(){
38744 this.resizeEl.unclip();
38748 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38749 * Will fail silently if the {@link #setUrl} method has not been called.
38750 * This does not activate the panel, just updates its content.
38752 refresh : function(){
38753 if(this.refreshDelegate){
38754 this.loaded = false;
38755 this.refreshDelegate();
38760 * Destroys this panel
38762 destroy : function(){
38763 this.el.removeAllListeners();
38764 var tempEl = document.createElement("span");
38765 tempEl.appendChild(this.el.dom);
38766 tempEl.innerHTML = "";
38772 * form - if the content panel contains a form - this is a reference to it.
38773 * @type {Roo.form.Form}
38777 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38778 * This contains a reference to it.
38784 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38794 * @param {Object} cfg Xtype definition of item to add.
38798 getChildContainer: function () {
38799 return this.getEl();
38804 var ret = new Roo.factory(cfg);
38809 if (cfg.xtype.match(/^Form$/)) {
38812 //if (this.footer) {
38813 // el = this.footer.container.insertSibling(false, 'before');
38815 el = this.el.createChild();
38818 this.form = new Roo.form.Form(cfg);
38821 if ( this.form.allItems.length) {
38822 this.form.render(el.dom);
38826 // should only have one of theses..
38827 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38828 // views.. should not be just added - used named prop 'view''
38830 cfg.el = this.el.appendChild(document.createElement("div"));
38833 var ret = new Roo.factory(cfg);
38835 ret.render && ret.render(false, ''); // render blank..
38845 * @class Roo.bootstrap.panel.Grid
38846 * @extends Roo.bootstrap.panel.Content
38848 * Create a new GridPanel.
38849 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38850 * @param {Object} config A the config object
38856 Roo.bootstrap.panel.Grid = function(config)
38860 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38861 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38863 config.el = this.wrapper;
38864 //this.el = this.wrapper;
38866 if (config.container) {
38867 // ctor'ed from a Border/panel.grid
38870 this.wrapper.setStyle("overflow", "hidden");
38871 this.wrapper.addClass('roo-grid-container');
38876 if(config.toolbar){
38877 var tool_el = this.wrapper.createChild();
38878 this.toolbar = Roo.factory(config.toolbar);
38880 if (config.toolbar.items) {
38881 ti = config.toolbar.items ;
38882 delete config.toolbar.items ;
38886 this.toolbar.render(tool_el);
38887 for(var i =0;i < ti.length;i++) {
38888 // Roo.log(['add child', items[i]]);
38889 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38891 this.toolbar.items = nitems;
38893 delete config.toolbar;
38896 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38897 config.grid.scrollBody = true;;
38898 config.grid.monitorWindowResize = false; // turn off autosizing
38899 config.grid.autoHeight = false;
38900 config.grid.autoWidth = false;
38902 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38904 if (config.background) {
38905 // render grid on panel activation (if panel background)
38906 this.on('activate', function(gp) {
38907 if (!gp.grid.rendered) {
38908 gp.grid.render(this.wrapper);
38909 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38914 this.grid.render(this.wrapper);
38915 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38918 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38919 // ??? needed ??? config.el = this.wrapper;
38924 // xtype created footer. - not sure if will work as we normally have to render first..
38925 if (this.footer && !this.footer.el && this.footer.xtype) {
38927 var ctr = this.grid.getView().getFooterPanel(true);
38928 this.footer.dataSource = this.grid.dataSource;
38929 this.footer = Roo.factory(this.footer, Roo);
38930 this.footer.render(ctr);
38940 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38941 getId : function(){
38942 return this.grid.id;
38946 * Returns the grid for this panel
38947 * @return {Roo.bootstrap.Table}
38949 getGrid : function(){
38953 setSize : function(width, height){
38954 if(!this.ignoreResize(width, height)){
38955 var grid = this.grid;
38956 var size = this.adjustForComponents(width, height);
38957 var gridel = grid.getGridEl();
38958 gridel.setSize(size.width, size.height);
38960 var thd = grid.getGridEl().select('thead',true).first();
38961 var tbd = grid.getGridEl().select('tbody', true).first();
38963 tbd.setSize(width, height - thd.getHeight());
38972 beforeSlide : function(){
38973 this.grid.getView().scroller.clip();
38976 afterSlide : function(){
38977 this.grid.getView().scroller.unclip();
38980 destroy : function(){
38981 this.grid.destroy();
38983 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38988 * @class Roo.bootstrap.panel.Nest
38989 * @extends Roo.bootstrap.panel.Content
38991 * Create a new Panel, that can contain a layout.Border.
38994 * @param {Roo.BorderLayout} layout The layout for this panel
38995 * @param {String/Object} config A string to set only the title or a config object
38997 Roo.bootstrap.panel.Nest = function(config)
38999 // construct with only one argument..
39000 /* FIXME - implement nicer consturctors
39001 if (layout.layout) {
39003 layout = config.layout;
39004 delete config.layout;
39006 if (layout.xtype && !layout.getEl) {
39007 // then layout needs constructing..
39008 layout = Roo.factory(layout, Roo);
39012 config.el = config.layout.getEl();
39014 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39016 config.layout.monitorWindowResize = false; // turn off autosizing
39017 this.layout = config.layout;
39018 this.layout.getEl().addClass("roo-layout-nested-layout");
39019 this.layout.parent = this;
39026 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39028 setSize : function(width, height){
39029 if(!this.ignoreResize(width, height)){
39030 var size = this.adjustForComponents(width, height);
39031 var el = this.layout.getEl();
39032 if (size.height < 1) {
39033 el.setWidth(size.width);
39035 el.setSize(size.width, size.height);
39037 var touch = el.dom.offsetWidth;
39038 this.layout.layout();
39039 // ie requires a double layout on the first pass
39040 if(Roo.isIE && !this.initialized){
39041 this.initialized = true;
39042 this.layout.layout();
39047 // activate all subpanels if not currently active..
39049 setActiveState : function(active){
39050 this.active = active;
39051 this.setActiveClass(active);
39054 this.fireEvent("deactivate", this);
39058 this.fireEvent("activate", this);
39059 // not sure if this should happen before or after..
39060 if (!this.layout) {
39061 return; // should not happen..
39064 for (var r in this.layout.regions) {
39065 reg = this.layout.getRegion(r);
39066 if (reg.getActivePanel()) {
39067 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39068 reg.setActivePanel(reg.getActivePanel());
39071 if (!reg.panels.length) {
39074 reg.showPanel(reg.getPanel(0));
39083 * Returns the nested BorderLayout for this panel
39084 * @return {Roo.BorderLayout}
39086 getLayout : function(){
39087 return this.layout;
39091 * Adds a xtype elements to the layout of the nested panel
39095 xtype : 'ContentPanel',
39102 xtype : 'NestedLayoutPanel',
39108 items : [ ... list of content panels or nested layout panels.. ]
39112 * @param {Object} cfg Xtype definition of item to add.
39114 addxtype : function(cfg) {
39115 return this.layout.addxtype(cfg);
39120 * Ext JS Library 1.1.1
39121 * Copyright(c) 2006-2007, Ext JS, LLC.
39123 * Originally Released Under LGPL - original licence link has changed is not relivant.
39126 * <script type="text/javascript">
39129 * @class Roo.TabPanel
39130 * @extends Roo.util.Observable
39131 * A lightweight tab container.
39135 // basic tabs 1, built from existing content
39136 var tabs = new Roo.TabPanel("tabs1");
39137 tabs.addTab("script", "View Script");
39138 tabs.addTab("markup", "View Markup");
39139 tabs.activate("script");
39141 // more advanced tabs, built from javascript
39142 var jtabs = new Roo.TabPanel("jtabs");
39143 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39145 // set up the UpdateManager
39146 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39147 var updater = tab2.getUpdateManager();
39148 updater.setDefaultUrl("ajax1.htm");
39149 tab2.on('activate', updater.refresh, updater, true);
39151 // Use setUrl for Ajax loading
39152 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39153 tab3.setUrl("ajax2.htm", null, true);
39156 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39159 jtabs.activate("jtabs-1");
39162 * Create a new TabPanel.
39163 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39164 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39166 Roo.bootstrap.panel.Tabs = function(config){
39168 * The container element for this TabPanel.
39169 * @type Roo.Element
39171 this.el = Roo.get(config.el);
39174 if(typeof config == "boolean"){
39175 this.tabPosition = config ? "bottom" : "top";
39177 Roo.apply(this, config);
39181 if(this.tabPosition == "bottom"){
39182 // if tabs are at the bottom = create the body first.
39183 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39184 this.el.addClass("roo-tabs-bottom");
39186 // next create the tabs holders
39188 if (this.tabPosition == "west"){
39190 var reg = this.region; // fake it..
39192 if (!reg.mgr.parent) {
39195 reg = reg.mgr.parent.region;
39197 Roo.log("got nest?");
39199 if (reg.mgr.getRegion('west')) {
39200 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39201 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39202 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39203 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39204 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39212 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39213 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39214 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39215 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39220 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39223 // finally - if tabs are at the top, then create the body last..
39224 if(this.tabPosition != "bottom"){
39225 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39226 * @type Roo.Element
39228 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39229 this.el.addClass("roo-tabs-top");
39233 this.bodyEl.setStyle("position", "relative");
39235 this.active = null;
39236 this.activateDelegate = this.activate.createDelegate(this);
39241 * Fires when the active tab changes
39242 * @param {Roo.TabPanel} this
39243 * @param {Roo.TabPanelItem} activePanel The new active tab
39247 * @event beforetabchange
39248 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39249 * @param {Roo.TabPanel} this
39250 * @param {Object} e Set cancel to true on this object to cancel the tab change
39251 * @param {Roo.TabPanelItem} tab The tab being changed to
39253 "beforetabchange" : true
39256 Roo.EventManager.onWindowResize(this.onResize, this);
39257 this.cpad = this.el.getPadding("lr");
39258 this.hiddenCount = 0;
39261 // toolbar on the tabbar support...
39262 if (this.toolbar) {
39263 alert("no toolbar support yet");
39264 this.toolbar = false;
39266 var tcfg = this.toolbar;
39267 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39268 this.toolbar = new Roo.Toolbar(tcfg);
39269 if (Roo.isSafari) {
39270 var tbl = tcfg.container.child('table', true);
39271 tbl.setAttribute('width', '100%');
39279 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39282 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39284 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39286 tabPosition : "top",
39288 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39290 currentTabWidth : 0,
39292 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39296 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39300 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39302 preferredTabWidth : 175,
39304 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39306 resizeTabs : false,
39308 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39310 monitorResize : true,
39312 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39314 toolbar : false, // set by caller..
39316 region : false, /// set by caller
39318 disableTooltips : true, // not used yet...
39321 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39322 * @param {String} id The id of the div to use <b>or create</b>
39323 * @param {String} text The text for the tab
39324 * @param {String} content (optional) Content to put in the TabPanelItem body
39325 * @param {Boolean} closable (optional) True to create a close icon on the tab
39326 * @return {Roo.TabPanelItem} The created TabPanelItem
39328 addTab : function(id, text, content, closable, tpl)
39330 var item = new Roo.bootstrap.panel.TabItem({
39334 closable : closable,
39337 this.addTabItem(item);
39339 item.setContent(content);
39345 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39346 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39347 * @return {Roo.TabPanelItem}
39349 getTab : function(id){
39350 return this.items[id];
39354 * Hides the {@link Roo.TabPanelItem} with the specified id/index
39355 * @param {String/Number} id The id or index of the TabPanelItem to hide.
39357 hideTab : function(id){
39358 var t = this.items[id];
39361 this.hiddenCount++;
39362 this.autoSizeTabs();
39367 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39368 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39370 unhideTab : function(id){
39371 var t = this.items[id];
39373 t.setHidden(false);
39374 this.hiddenCount--;
39375 this.autoSizeTabs();
39380 * Adds an existing {@link Roo.TabPanelItem}.
39381 * @param {Roo.TabPanelItem} item The TabPanelItem to add
39383 addTabItem : function(item)
39385 this.items[item.id] = item;
39386 this.items.push(item);
39387 this.autoSizeTabs();
39388 // if(this.resizeTabs){
39389 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39390 // this.autoSizeTabs();
39392 // item.autoSize();
39397 * Removes a {@link Roo.TabPanelItem}.
39398 * @param {String/Number} id The id or index of the TabPanelItem to remove.
39400 removeTab : function(id){
39401 var items = this.items;
39402 var tab = items[id];
39403 if(!tab) { return; }
39404 var index = items.indexOf(tab);
39405 if(this.active == tab && items.length > 1){
39406 var newTab = this.getNextAvailable(index);
39411 this.stripEl.dom.removeChild(tab.pnode.dom);
39412 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39413 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39415 items.splice(index, 1);
39416 delete this.items[tab.id];
39417 tab.fireEvent("close", tab);
39418 tab.purgeListeners();
39419 this.autoSizeTabs();
39422 getNextAvailable : function(start){
39423 var items = this.items;
39425 // look for a next tab that will slide over to
39426 // replace the one being removed
39427 while(index < items.length){
39428 var item = items[++index];
39429 if(item && !item.isHidden()){
39433 // if one isn't found select the previous tab (on the left)
39436 var item = items[--index];
39437 if(item && !item.isHidden()){
39445 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39446 * @param {String/Number} id The id or index of the TabPanelItem to disable.
39448 disableTab : function(id){
39449 var tab = this.items[id];
39450 if(tab && this.active != tab){
39456 * Enables a {@link Roo.TabPanelItem} that is disabled.
39457 * @param {String/Number} id The id or index of the TabPanelItem to enable.
39459 enableTab : function(id){
39460 var tab = this.items[id];
39465 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39466 * @param {String/Number} id The id or index of the TabPanelItem to activate.
39467 * @return {Roo.TabPanelItem} The TabPanelItem.
39469 activate : function(id)
39471 //Roo.log('activite:' + id);
39473 var tab = this.items[id];
39477 if(tab == this.active || tab.disabled){
39481 this.fireEvent("beforetabchange", this, e, tab);
39482 if(e.cancel !== true && !tab.disabled){
39484 this.active.hide();
39486 this.active = this.items[id];
39487 this.active.show();
39488 this.fireEvent("tabchange", this, this.active);
39494 * Gets the active {@link Roo.TabPanelItem}.
39495 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39497 getActiveTab : function(){
39498 return this.active;
39502 * Updates the tab body element to fit the height of the container element
39503 * for overflow scrolling
39504 * @param {Number} targetHeight (optional) Override the starting height from the elements height
39506 syncHeight : function(targetHeight){
39507 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39508 var bm = this.bodyEl.getMargins();
39509 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39510 this.bodyEl.setHeight(newHeight);
39514 onResize : function(){
39515 if(this.monitorResize){
39516 this.autoSizeTabs();
39521 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39523 beginUpdate : function(){
39524 this.updating = true;
39528 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39530 endUpdate : function(){
39531 this.updating = false;
39532 this.autoSizeTabs();
39536 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39538 autoSizeTabs : function()
39540 var count = this.items.length;
39541 var vcount = count - this.hiddenCount;
39544 this.stripEl.hide();
39546 this.stripEl.show();
39549 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39554 var w = Math.max(this.el.getWidth() - this.cpad, 10);
39555 var availWidth = Math.floor(w / vcount);
39556 var b = this.stripBody;
39557 if(b.getWidth() > w){
39558 var tabs = this.items;
39559 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39560 if(availWidth < this.minTabWidth){
39561 /*if(!this.sleft){ // incomplete scrolling code
39562 this.createScrollButtons();
39565 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39568 if(this.currentTabWidth < this.preferredTabWidth){
39569 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39575 * Returns the number of tabs in this TabPanel.
39578 getCount : function(){
39579 return this.items.length;
39583 * Resizes all the tabs to the passed width
39584 * @param {Number} The new width
39586 setTabWidth : function(width){
39587 this.currentTabWidth = width;
39588 for(var i = 0, len = this.items.length; i < len; i++) {
39589 if(!this.items[i].isHidden()) {
39590 this.items[i].setWidth(width);
39596 * Destroys this TabPanel
39597 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39599 destroy : function(removeEl){
39600 Roo.EventManager.removeResizeListener(this.onResize, this);
39601 for(var i = 0, len = this.items.length; i < len; i++){
39602 this.items[i].purgeListeners();
39604 if(removeEl === true){
39605 this.el.update("");
39610 createStrip : function(container)
39612 var strip = document.createElement("nav");
39613 strip.className = Roo.bootstrap.version == 4 ?
39614 "navbar-light bg-light" :
39615 "navbar navbar-default"; //"x-tabs-wrap";
39616 container.appendChild(strip);
39620 createStripList : function(strip)
39622 // div wrapper for retard IE
39623 // returns the "tr" element.
39624 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39625 //'<div class="x-tabs-strip-wrap">'+
39626 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39627 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39628 return strip.firstChild; //.firstChild.firstChild.firstChild;
39630 createBody : function(container)
39632 var body = document.createElement("div");
39633 Roo.id(body, "tab-body");
39634 //Roo.fly(body).addClass("x-tabs-body");
39635 Roo.fly(body).addClass("tab-content");
39636 container.appendChild(body);
39639 createItemBody :function(bodyEl, id){
39640 var body = Roo.getDom(id);
39642 body = document.createElement("div");
39645 //Roo.fly(body).addClass("x-tabs-item-body");
39646 Roo.fly(body).addClass("tab-pane");
39647 bodyEl.insertBefore(body, bodyEl.firstChild);
39651 createStripElements : function(stripEl, text, closable, tpl)
39653 var td = document.createElement("li"); // was td..
39654 td.className = 'nav-item';
39656 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39659 stripEl.appendChild(td);
39661 td.className = "x-tabs-closable";
39662 if(!this.closeTpl){
39663 this.closeTpl = new Roo.Template(
39664 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39665 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39666 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39669 var el = this.closeTpl.overwrite(td, {"text": text});
39670 var close = el.getElementsByTagName("div")[0];
39671 var inner = el.getElementsByTagName("em")[0];
39672 return {"el": el, "close": close, "inner": inner};
39675 // not sure what this is..
39676 // if(!this.tabTpl){
39677 //this.tabTpl = new Roo.Template(
39678 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39679 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39681 // this.tabTpl = new Roo.Template(
39682 // '<a href="#">' +
39683 // '<span unselectable="on"' +
39684 // (this.disableTooltips ? '' : ' title="{text}"') +
39685 // ' >{text}</span></a>'
39691 var template = tpl || this.tabTpl || false;
39694 template = new Roo.Template(
39695 Roo.bootstrap.version == 4 ?
39697 '<a class="nav-link" href="#" unselectable="on"' +
39698 (this.disableTooltips ? '' : ' title="{text}"') +
39701 '<a class="nav-link" href="#">' +
39702 '<span unselectable="on"' +
39703 (this.disableTooltips ? '' : ' title="{text}"') +
39704 ' >{text}</span></a>'
39709 switch (typeof(template)) {
39713 template = new Roo.Template(template);
39719 var el = template.overwrite(td, {"text": text});
39721 var inner = el.getElementsByTagName("span")[0];
39723 return {"el": el, "inner": inner};
39731 * @class Roo.TabPanelItem
39732 * @extends Roo.util.Observable
39733 * Represents an individual item (tab plus body) in a TabPanel.
39734 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39735 * @param {String} id The id of this TabPanelItem
39736 * @param {String} text The text for the tab of this TabPanelItem
39737 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39739 Roo.bootstrap.panel.TabItem = function(config){
39741 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39742 * @type Roo.TabPanel
39744 this.tabPanel = config.panel;
39746 * The id for this TabPanelItem
39749 this.id = config.id;
39751 this.disabled = false;
39753 this.text = config.text;
39755 this.loaded = false;
39756 this.closable = config.closable;
39759 * The body element for this TabPanelItem.
39760 * @type Roo.Element
39762 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39763 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39764 this.bodyEl.setStyle("display", "block");
39765 this.bodyEl.setStyle("zoom", "1");
39766 //this.hideAction();
39768 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39770 this.el = Roo.get(els.el);
39771 this.inner = Roo.get(els.inner, true);
39772 this.textEl = Roo.bootstrap.version == 4 ?
39773 this.el : Roo.get(this.el.dom.firstChild, true);
39775 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39776 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39779 // this.el.on("mousedown", this.onTabMouseDown, this);
39780 this.el.on("click", this.onTabClick, this);
39782 if(config.closable){
39783 var c = Roo.get(els.close, true);
39784 c.dom.title = this.closeText;
39785 c.addClassOnOver("close-over");
39786 c.on("click", this.closeClick, this);
39792 * Fires when this tab becomes the active tab.
39793 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39794 * @param {Roo.TabPanelItem} this
39798 * @event beforeclose
39799 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39800 * @param {Roo.TabPanelItem} this
39801 * @param {Object} e Set cancel to true on this object to cancel the close.
39803 "beforeclose": true,
39806 * Fires when this tab is closed.
39807 * @param {Roo.TabPanelItem} this
39811 * @event deactivate
39812 * Fires when this tab is no longer the active tab.
39813 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39814 * @param {Roo.TabPanelItem} this
39816 "deactivate" : true
39818 this.hidden = false;
39820 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39823 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39825 purgeListeners : function(){
39826 Roo.util.Observable.prototype.purgeListeners.call(this);
39827 this.el.removeAllListeners();
39830 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39833 this.status_node.addClass("active");
39836 this.tabPanel.stripWrap.repaint();
39838 this.fireEvent("activate", this.tabPanel, this);
39842 * Returns true if this tab is the active tab.
39843 * @return {Boolean}
39845 isActive : function(){
39846 return this.tabPanel.getActiveTab() == this;
39850 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39853 this.status_node.removeClass("active");
39855 this.fireEvent("deactivate", this.tabPanel, this);
39858 hideAction : function(){
39859 this.bodyEl.hide();
39860 this.bodyEl.setStyle("position", "absolute");
39861 this.bodyEl.setLeft("-20000px");
39862 this.bodyEl.setTop("-20000px");
39865 showAction : function(){
39866 this.bodyEl.setStyle("position", "relative");
39867 this.bodyEl.setTop("");
39868 this.bodyEl.setLeft("");
39869 this.bodyEl.show();
39873 * Set the tooltip for the tab.
39874 * @param {String} tooltip The tab's tooltip
39876 setTooltip : function(text){
39877 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39878 this.textEl.dom.qtip = text;
39879 this.textEl.dom.removeAttribute('title');
39881 this.textEl.dom.title = text;
39885 onTabClick : function(e){
39886 e.preventDefault();
39887 this.tabPanel.activate(this.id);
39890 onTabMouseDown : function(e){
39891 e.preventDefault();
39892 this.tabPanel.activate(this.id);
39895 getWidth : function(){
39896 return this.inner.getWidth();
39899 setWidth : function(width){
39900 var iwidth = width - this.linode.getPadding("lr");
39901 this.inner.setWidth(iwidth);
39902 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39903 this.linode.setWidth(width);
39907 * Show or hide the tab
39908 * @param {Boolean} hidden True to hide or false to show.
39910 setHidden : function(hidden){
39911 this.hidden = hidden;
39912 this.linode.setStyle("display", hidden ? "none" : "");
39916 * Returns true if this tab is "hidden"
39917 * @return {Boolean}
39919 isHidden : function(){
39920 return this.hidden;
39924 * Returns the text for this tab
39927 getText : function(){
39931 autoSize : function(){
39932 //this.el.beginMeasure();
39933 this.textEl.setWidth(1);
39935 * #2804 [new] Tabs in Roojs
39936 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39938 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39939 //this.el.endMeasure();
39943 * Sets the text for the tab (Note: this also sets the tooltip text)
39944 * @param {String} text The tab's text and tooltip
39946 setText : function(text){
39948 this.textEl.update(text);
39949 this.setTooltip(text);
39950 //if(!this.tabPanel.resizeTabs){
39951 // this.autoSize();
39955 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39957 activate : function(){
39958 this.tabPanel.activate(this.id);
39962 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39964 disable : function(){
39965 if(this.tabPanel.active != this){
39966 this.disabled = true;
39967 this.status_node.addClass("disabled");
39972 * Enables this TabPanelItem if it was previously disabled.
39974 enable : function(){
39975 this.disabled = false;
39976 this.status_node.removeClass("disabled");
39980 * Sets the content for this TabPanelItem.
39981 * @param {String} content The content
39982 * @param {Boolean} loadScripts true to look for and load scripts
39984 setContent : function(content, loadScripts){
39985 this.bodyEl.update(content, loadScripts);
39989 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39990 * @return {Roo.UpdateManager} The UpdateManager
39992 getUpdateManager : function(){
39993 return this.bodyEl.getUpdateManager();
39997 * Set a URL to be used to load the content for this TabPanelItem.
39998 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39999 * @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)
40000 * @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)
40001 * @return {Roo.UpdateManager} The UpdateManager
40003 setUrl : function(url, params, loadOnce){
40004 if(this.refreshDelegate){
40005 this.un('activate', this.refreshDelegate);
40007 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40008 this.on("activate", this.refreshDelegate);
40009 return this.bodyEl.getUpdateManager();
40013 _handleRefresh : function(url, params, loadOnce){
40014 if(!loadOnce || !this.loaded){
40015 var updater = this.bodyEl.getUpdateManager();
40016 updater.update(url, params, this._setLoaded.createDelegate(this));
40021 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40022 * Will fail silently if the setUrl method has not been called.
40023 * This does not activate the panel, just updates its content.
40025 refresh : function(){
40026 if(this.refreshDelegate){
40027 this.loaded = false;
40028 this.refreshDelegate();
40033 _setLoaded : function(){
40034 this.loaded = true;
40038 closeClick : function(e){
40041 this.fireEvent("beforeclose", this, o);
40042 if(o.cancel !== true){
40043 this.tabPanel.removeTab(this.id);
40047 * The text displayed in the tooltip for the close icon.
40050 closeText : "Close this tab"
40053 * This script refer to:
40054 * Title: International Telephone Input
40055 * Author: Jack O'Connor
40056 * Code version: v12.1.12
40057 * Availability: https://github.com/jackocnr/intl-tel-input.git
40060 Roo.bootstrap.PhoneInputData = function() {
40063 "Afghanistan (افغانستان)",
40068 "Albania (Shqipëri)",
40073 "Algeria (الجزائر)",
40098 "Antigua and Barbuda",
40108 "Armenia (Հայաստան)",
40124 "Austria (Österreich)",
40129 "Azerbaijan (Azərbaycan)",
40139 "Bahrain (البحرين)",
40144 "Bangladesh (বাংলাদেশ)",
40154 "Belarus (Беларусь)",
40159 "Belgium (België)",
40189 "Bosnia and Herzegovina (Босна и Херцеговина)",
40204 "British Indian Ocean Territory",
40209 "British Virgin Islands",
40219 "Bulgaria (България)",
40229 "Burundi (Uburundi)",
40234 "Cambodia (កម្ពុជា)",
40239 "Cameroon (Cameroun)",
40248 ["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"]
40251 "Cape Verde (Kabu Verdi)",
40256 "Caribbean Netherlands",
40267 "Central African Republic (République centrafricaine)",
40287 "Christmas Island",
40293 "Cocos (Keeling) Islands",
40304 "Comoros (جزر القمر)",
40309 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40314 "Congo (Republic) (Congo-Brazzaville)",
40334 "Croatia (Hrvatska)",
40355 "Czech Republic (Česká republika)",
40360 "Denmark (Danmark)",
40375 "Dominican Republic (República Dominicana)",
40379 ["809", "829", "849"]
40397 "Equatorial Guinea (Guinea Ecuatorial)",
40417 "Falkland Islands (Islas Malvinas)",
40422 "Faroe Islands (Føroyar)",
40443 "French Guiana (Guyane française)",
40448 "French Polynesia (Polynésie française)",
40463 "Georgia (საქართველო)",
40468 "Germany (Deutschland)",
40488 "Greenland (Kalaallit Nunaat)",
40525 "Guinea-Bissau (Guiné Bissau)",
40550 "Hungary (Magyarország)",
40555 "Iceland (Ísland)",
40575 "Iraq (العراق)",
40591 "Israel (ישראל)",
40618 "Jordan (الأردن)",
40623 "Kazakhstan (Казахстан)",
40644 "Kuwait (الكويت)",
40649 "Kyrgyzstan (Кыргызстан)",
40659 "Latvia (Latvija)",
40664 "Lebanon (لبنان)",
40679 "Libya (ليبيا)",
40689 "Lithuania (Lietuva)",
40704 "Macedonia (FYROM) (Македонија)",
40709 "Madagascar (Madagasikara)",
40739 "Marshall Islands",
40749 "Mauritania (موريتانيا)",
40754 "Mauritius (Moris)",
40775 "Moldova (Republica Moldova)",
40785 "Mongolia (Монгол)",
40790 "Montenegro (Crna Gora)",
40800 "Morocco (المغرب)",
40806 "Mozambique (Moçambique)",
40811 "Myanmar (Burma) (မြန်မာ)",
40816 "Namibia (Namibië)",
40831 "Netherlands (Nederland)",
40836 "New Caledonia (Nouvelle-Calédonie)",
40871 "North Korea (조선 민주주의 인민 공화국)",
40876 "Northern Mariana Islands",
40892 "Pakistan (پاکستان)",
40902 "Palestine (فلسطين)",
40912 "Papua New Guinea",
40954 "Réunion (La Réunion)",
40960 "Romania (România)",
40976 "Saint Barthélemy",
40987 "Saint Kitts and Nevis",
40997 "Saint Martin (Saint-Martin (partie française))",
41003 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41008 "Saint Vincent and the Grenadines",
41023 "São Tomé and Príncipe (São Tomé e Príncipe)",
41028 "Saudi Arabia (المملكة العربية السعودية)",
41033 "Senegal (Sénégal)",
41063 "Slovakia (Slovensko)",
41068 "Slovenia (Slovenija)",
41078 "Somalia (Soomaaliya)",
41088 "South Korea (대한민국)",
41093 "South Sudan (جنوب السودان)",
41103 "Sri Lanka (ශ්රී ලංකාව)",
41108 "Sudan (السودان)",
41118 "Svalbard and Jan Mayen",
41129 "Sweden (Sverige)",
41134 "Switzerland (Schweiz)",
41139 "Syria (سوريا)",
41184 "Trinidad and Tobago",
41189 "Tunisia (تونس)",
41194 "Turkey (Türkiye)",
41204 "Turks and Caicos Islands",
41214 "U.S. Virgin Islands",
41224 "Ukraine (Україна)",
41229 "United Arab Emirates (الإمارات العربية المتحدة)",
41251 "Uzbekistan (Oʻzbekiston)",
41261 "Vatican City (Città del Vaticano)",
41272 "Vietnam (Việt Nam)",
41277 "Wallis and Futuna (Wallis-et-Futuna)",
41282 "Western Sahara (الصحراء الغربية)",
41288 "Yemen (اليمن)",
41312 * This script refer to:
41313 * Title: International Telephone Input
41314 * Author: Jack O'Connor
41315 * Code version: v12.1.12
41316 * Availability: https://github.com/jackocnr/intl-tel-input.git
41320 * @class Roo.bootstrap.PhoneInput
41321 * @extends Roo.bootstrap.TriggerField
41322 * An input with International dial-code selection
41324 * @cfg {String} defaultDialCode default '+852'
41325 * @cfg {Array} preferedCountries default []
41328 * Create a new PhoneInput.
41329 * @param {Object} config Configuration options
41332 Roo.bootstrap.PhoneInput = function(config) {
41333 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41336 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41338 listWidth: undefined,
41340 selectedClass: 'active',
41342 invalidClass : "has-warning",
41344 validClass: 'has-success',
41346 allowed: '0123456789',
41351 * @cfg {String} defaultDialCode The default dial code when initializing the input
41353 defaultDialCode: '+852',
41356 * @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
41358 preferedCountries: false,
41360 getAutoCreate : function()
41362 var data = Roo.bootstrap.PhoneInputData();
41363 var align = this.labelAlign || this.parentLabelAlign();
41366 this.allCountries = [];
41367 this.dialCodeMapping = [];
41369 for (var i = 0; i < data.length; i++) {
41371 this.allCountries[i] = {
41375 priority: c[3] || 0,
41376 areaCodes: c[4] || null
41378 this.dialCodeMapping[c[2]] = {
41381 priority: c[3] || 0,
41382 areaCodes: c[4] || null
41394 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41395 maxlength: this.max_length,
41396 cls : 'form-control tel-input',
41397 autocomplete: 'new-password'
41400 var hiddenInput = {
41403 cls: 'hidden-tel-input'
41407 hiddenInput.name = this.name;
41410 if (this.disabled) {
41411 input.disabled = true;
41414 var flag_container = {
41431 cls: this.hasFeedback ? 'has-feedback' : '',
41437 cls: 'dial-code-holder',
41444 cls: 'roo-select2-container input-group',
41451 if (this.fieldLabel.length) {
41454 tooltip: 'This field is required'
41460 cls: 'control-label',
41466 html: this.fieldLabel
41469 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41475 if(this.indicatorpos == 'right') {
41476 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41483 if(align == 'left') {
41491 if(this.labelWidth > 12){
41492 label.style = "width: " + this.labelWidth + 'px';
41494 if(this.labelWidth < 13 && this.labelmd == 0){
41495 this.labelmd = this.labelWidth;
41497 if(this.labellg > 0){
41498 label.cls += ' col-lg-' + this.labellg;
41499 input.cls += ' col-lg-' + (12 - this.labellg);
41501 if(this.labelmd > 0){
41502 label.cls += ' col-md-' + this.labelmd;
41503 container.cls += ' col-md-' + (12 - this.labelmd);
41505 if(this.labelsm > 0){
41506 label.cls += ' col-sm-' + this.labelsm;
41507 container.cls += ' col-sm-' + (12 - this.labelsm);
41509 if(this.labelxs > 0){
41510 label.cls += ' col-xs-' + this.labelxs;
41511 container.cls += ' col-xs-' + (12 - this.labelxs);
41521 var settings = this;
41523 ['xs','sm','md','lg'].map(function(size){
41524 if (settings[size]) {
41525 cfg.cls += ' col-' + size + '-' + settings[size];
41529 this.store = new Roo.data.Store({
41530 proxy : new Roo.data.MemoryProxy({}),
41531 reader : new Roo.data.JsonReader({
41542 'name' : 'dialCode',
41546 'name' : 'priority',
41550 'name' : 'areaCodes',
41557 if(!this.preferedCountries) {
41558 this.preferedCountries = [
41565 var p = this.preferedCountries.reverse();
41568 for (var i = 0; i < p.length; i++) {
41569 for (var j = 0; j < this.allCountries.length; j++) {
41570 if(this.allCountries[j].iso2 == p[i]) {
41571 var t = this.allCountries[j];
41572 this.allCountries.splice(j,1);
41573 this.allCountries.unshift(t);
41579 this.store.proxy.data = {
41581 data: this.allCountries
41587 initEvents : function()
41590 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41592 this.indicator = this.indicatorEl();
41593 this.flag = this.flagEl();
41594 this.dialCodeHolder = this.dialCodeHolderEl();
41596 this.trigger = this.el.select('div.flag-box',true).first();
41597 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41602 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41603 _this.list.setWidth(lw);
41606 this.list.on('mouseover', this.onViewOver, this);
41607 this.list.on('mousemove', this.onViewMove, this);
41608 this.inputEl().on("keyup", this.onKeyUp, this);
41609 this.inputEl().on("keypress", this.onKeyPress, this);
41611 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41613 this.view = new Roo.View(this.list, this.tpl, {
41614 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41617 this.view.on('click', this.onViewClick, this);
41618 this.setValue(this.defaultDialCode);
41621 onTriggerClick : function(e)
41623 Roo.log('trigger click');
41628 if(this.isExpanded()){
41630 this.hasFocus = false;
41632 this.store.load({});
41633 this.hasFocus = true;
41638 isExpanded : function()
41640 return this.list.isVisible();
41643 collapse : function()
41645 if(!this.isExpanded()){
41649 Roo.get(document).un('mousedown', this.collapseIf, this);
41650 Roo.get(document).un('mousewheel', this.collapseIf, this);
41651 this.fireEvent('collapse', this);
41655 expand : function()
41659 if(this.isExpanded() || !this.hasFocus){
41663 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41664 this.list.setWidth(lw);
41667 this.restrictHeight();
41669 Roo.get(document).on('mousedown', this.collapseIf, this);
41670 Roo.get(document).on('mousewheel', this.collapseIf, this);
41672 this.fireEvent('expand', this);
41675 restrictHeight : function()
41677 this.list.alignTo(this.inputEl(), this.listAlign);
41678 this.list.alignTo(this.inputEl(), this.listAlign);
41681 onViewOver : function(e, t)
41683 if(this.inKeyMode){
41686 var item = this.view.findItemFromChild(t);
41689 var index = this.view.indexOf(item);
41690 this.select(index, false);
41695 onViewClick : function(view, doFocus, el, e)
41697 var index = this.view.getSelectedIndexes()[0];
41699 var r = this.store.getAt(index);
41702 this.onSelect(r, index);
41704 if(doFocus !== false && !this.blockFocus){
41705 this.inputEl().focus();
41709 onViewMove : function(e, t)
41711 this.inKeyMode = false;
41714 select : function(index, scrollIntoView)
41716 this.selectedIndex = index;
41717 this.view.select(index);
41718 if(scrollIntoView !== false){
41719 var el = this.view.getNode(index);
41721 this.list.scrollChildIntoView(el, false);
41726 createList : function()
41728 this.list = Roo.get(document.body).createChild({
41730 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41731 style: 'display:none'
41734 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41737 collapseIf : function(e)
41739 var in_combo = e.within(this.el);
41740 var in_list = e.within(this.list);
41741 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41743 if (in_combo || in_list || is_list) {
41749 onSelect : function(record, index)
41751 if(this.fireEvent('beforeselect', this, record, index) !== false){
41753 this.setFlagClass(record.data.iso2);
41754 this.setDialCode(record.data.dialCode);
41755 this.hasFocus = false;
41757 this.fireEvent('select', this, record, index);
41761 flagEl : function()
41763 var flag = this.el.select('div.flag',true).first();
41770 dialCodeHolderEl : function()
41772 var d = this.el.select('input.dial-code-holder',true).first();
41779 setDialCode : function(v)
41781 this.dialCodeHolder.dom.value = '+'+v;
41784 setFlagClass : function(n)
41786 this.flag.dom.className = 'flag '+n;
41789 getValue : function()
41791 var v = this.inputEl().getValue();
41792 if(this.dialCodeHolder) {
41793 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41798 setValue : function(v)
41800 var d = this.getDialCode(v);
41802 //invalid dial code
41803 if(v.length == 0 || !d || d.length == 0) {
41805 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41806 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41812 this.setFlagClass(this.dialCodeMapping[d].iso2);
41813 this.setDialCode(d);
41814 this.inputEl().dom.value = v.replace('+'+d,'');
41815 this.hiddenEl().dom.value = this.getValue();
41820 getDialCode : function(v)
41824 if (v.length == 0) {
41825 return this.dialCodeHolder.dom.value;
41829 if (v.charAt(0) != "+") {
41832 var numericChars = "";
41833 for (var i = 1; i < v.length; i++) {
41834 var c = v.charAt(i);
41837 if (this.dialCodeMapping[numericChars]) {
41838 dialCode = v.substr(1, i);
41840 if (numericChars.length == 4) {
41850 this.setValue(this.defaultDialCode);
41854 hiddenEl : function()
41856 return this.el.select('input.hidden-tel-input',true).first();
41859 // after setting val
41860 onKeyUp : function(e){
41861 this.setValue(this.getValue());
41864 onKeyPress : function(e){
41865 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41872 * @class Roo.bootstrap.MoneyField
41873 * @extends Roo.bootstrap.ComboBox
41874 * Bootstrap MoneyField class
41877 * Create a new MoneyField.
41878 * @param {Object} config Configuration options
41881 Roo.bootstrap.MoneyField = function(config) {
41883 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41887 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41890 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41892 allowDecimals : true,
41894 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41896 decimalSeparator : ".",
41898 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41900 decimalPrecision : 0,
41902 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41904 allowNegative : true,
41906 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41910 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41912 minValue : Number.NEGATIVE_INFINITY,
41914 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41916 maxValue : Number.MAX_VALUE,
41918 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41920 minText : "The minimum value for this field is {0}",
41922 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41924 maxText : "The maximum value for this field is {0}",
41926 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41927 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41929 nanText : "{0} is not a valid number",
41931 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41935 * @cfg {String} defaults currency of the MoneyField
41936 * value should be in lkey
41938 defaultCurrency : false,
41940 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41942 thousandsDelimiter : false,
41944 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41955 getAutoCreate : function()
41957 var align = this.labelAlign || this.parentLabelAlign();
41969 cls : 'form-control roo-money-amount-input',
41970 autocomplete: 'new-password'
41973 var hiddenInput = {
41977 cls: 'hidden-number-input'
41980 if(this.max_length) {
41981 input.maxlength = this.max_length;
41985 hiddenInput.name = this.name;
41988 if (this.disabled) {
41989 input.disabled = true;
41992 var clg = 12 - this.inputlg;
41993 var cmd = 12 - this.inputmd;
41994 var csm = 12 - this.inputsm;
41995 var cxs = 12 - this.inputxs;
41999 cls : 'row roo-money-field',
42003 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42007 cls: 'roo-select2-container input-group',
42011 cls : 'form-control roo-money-currency-input',
42012 autocomplete: 'new-password',
42014 name : this.currencyName
42018 cls : 'input-group-addon',
42032 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42036 cls: this.hasFeedback ? 'has-feedback' : '',
42047 if (this.fieldLabel.length) {
42050 tooltip: 'This field is required'
42056 cls: 'control-label',
42062 html: this.fieldLabel
42065 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42071 if(this.indicatorpos == 'right') {
42072 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42079 if(align == 'left') {
42087 if(this.labelWidth > 12){
42088 label.style = "width: " + this.labelWidth + 'px';
42090 if(this.labelWidth < 13 && this.labelmd == 0){
42091 this.labelmd = this.labelWidth;
42093 if(this.labellg > 0){
42094 label.cls += ' col-lg-' + this.labellg;
42095 input.cls += ' col-lg-' + (12 - this.labellg);
42097 if(this.labelmd > 0){
42098 label.cls += ' col-md-' + this.labelmd;
42099 container.cls += ' col-md-' + (12 - this.labelmd);
42101 if(this.labelsm > 0){
42102 label.cls += ' col-sm-' + this.labelsm;
42103 container.cls += ' col-sm-' + (12 - this.labelsm);
42105 if(this.labelxs > 0){
42106 label.cls += ' col-xs-' + this.labelxs;
42107 container.cls += ' col-xs-' + (12 - this.labelxs);
42118 var settings = this;
42120 ['xs','sm','md','lg'].map(function(size){
42121 if (settings[size]) {
42122 cfg.cls += ' col-' + size + '-' + settings[size];
42129 initEvents : function()
42131 this.indicator = this.indicatorEl();
42133 this.initCurrencyEvent();
42135 this.initNumberEvent();
42138 initCurrencyEvent : function()
42141 throw "can not find store for combo";
42144 this.store = Roo.factory(this.store, Roo.data);
42145 this.store.parent = this;
42149 this.triggerEl = this.el.select('.input-group-addon', true).first();
42151 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42156 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42157 _this.list.setWidth(lw);
42160 this.list.on('mouseover', this.onViewOver, this);
42161 this.list.on('mousemove', this.onViewMove, this);
42162 this.list.on('scroll', this.onViewScroll, this);
42165 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42168 this.view = new Roo.View(this.list, this.tpl, {
42169 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42172 this.view.on('click', this.onViewClick, this);
42174 this.store.on('beforeload', this.onBeforeLoad, this);
42175 this.store.on('load', this.onLoad, this);
42176 this.store.on('loadexception', this.onLoadException, this);
42178 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42179 "up" : function(e){
42180 this.inKeyMode = true;
42184 "down" : function(e){
42185 if(!this.isExpanded()){
42186 this.onTriggerClick();
42188 this.inKeyMode = true;
42193 "enter" : function(e){
42196 if(this.fireEvent("specialkey", this, e)){
42197 this.onViewClick(false);
42203 "esc" : function(e){
42207 "tab" : function(e){
42210 if(this.fireEvent("specialkey", this, e)){
42211 this.onViewClick(false);
42219 doRelay : function(foo, bar, hname){
42220 if(hname == 'down' || this.scope.isExpanded()){
42221 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42229 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42233 initNumberEvent : function(e)
42235 this.inputEl().on("keydown" , this.fireKey, this);
42236 this.inputEl().on("focus", this.onFocus, this);
42237 this.inputEl().on("blur", this.onBlur, this);
42239 this.inputEl().relayEvent('keyup', this);
42241 if(this.indicator){
42242 this.indicator.addClass('invisible');
42245 this.originalValue = this.getValue();
42247 if(this.validationEvent == 'keyup'){
42248 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42249 this.inputEl().on('keyup', this.filterValidation, this);
42251 else if(this.validationEvent !== false){
42252 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42255 if(this.selectOnFocus){
42256 this.on("focus", this.preFocus, this);
42259 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42260 this.inputEl().on("keypress", this.filterKeys, this);
42262 this.inputEl().relayEvent('keypress', this);
42265 var allowed = "0123456789";
42267 if(this.allowDecimals){
42268 allowed += this.decimalSeparator;
42271 if(this.allowNegative){
42275 if(this.thousandsDelimiter) {
42279 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42281 var keyPress = function(e){
42283 var k = e.getKey();
42285 var c = e.getCharCode();
42288 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42289 allowed.indexOf(String.fromCharCode(c)) === -1
42295 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42299 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42304 this.inputEl().on("keypress", keyPress, this);
42308 onTriggerClick : function(e)
42315 this.loadNext = false;
42317 if(this.isExpanded()){
42322 this.hasFocus = true;
42324 if(this.triggerAction == 'all') {
42325 this.doQuery(this.allQuery, true);
42329 this.doQuery(this.getRawValue());
42332 getCurrency : function()
42334 var v = this.currencyEl().getValue();
42339 restrictHeight : function()
42341 this.list.alignTo(this.currencyEl(), this.listAlign);
42342 this.list.alignTo(this.currencyEl(), this.listAlign);
42345 onViewClick : function(view, doFocus, el, e)
42347 var index = this.view.getSelectedIndexes()[0];
42349 var r = this.store.getAt(index);
42352 this.onSelect(r, index);
42356 onSelect : function(record, index){
42358 if(this.fireEvent('beforeselect', this, record, index) !== false){
42360 this.setFromCurrencyData(index > -1 ? record.data : false);
42364 this.fireEvent('select', this, record, index);
42368 setFromCurrencyData : function(o)
42372 this.lastCurrency = o;
42374 if (this.currencyField) {
42375 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42377 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
42380 this.lastSelectionText = currency;
42382 //setting default currency
42383 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42384 this.setCurrency(this.defaultCurrency);
42388 this.setCurrency(currency);
42391 setFromData : function(o)
42395 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42397 this.setFromCurrencyData(c);
42402 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42404 Roo.log('no value set for '+ (this.name ? this.name : this.id));
42407 this.setValue(value);
42411 setCurrency : function(v)
42413 this.currencyValue = v;
42416 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42421 setValue : function(v)
42423 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42429 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42431 this.inputEl().dom.value = (v == '') ? '' :
42432 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42434 if(!this.allowZero && v === '0') {
42435 this.hiddenEl().dom.value = '';
42436 this.inputEl().dom.value = '';
42443 getRawValue : function()
42445 var v = this.inputEl().getValue();
42450 getValue : function()
42452 return this.fixPrecision(this.parseValue(this.getRawValue()));
42455 parseValue : function(value)
42457 if(this.thousandsDelimiter) {
42459 r = new RegExp(",", "g");
42460 value = value.replace(r, "");
42463 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42464 return isNaN(value) ? '' : value;
42468 fixPrecision : function(value)
42470 if(this.thousandsDelimiter) {
42472 r = new RegExp(",", "g");
42473 value = value.replace(r, "");
42476 var nan = isNaN(value);
42478 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42479 return nan ? '' : value;
42481 return parseFloat(value).toFixed(this.decimalPrecision);
42484 decimalPrecisionFcn : function(v)
42486 return Math.floor(v);
42489 validateValue : function(value)
42491 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42495 var num = this.parseValue(value);
42498 this.markInvalid(String.format(this.nanText, value));
42502 if(num < this.minValue){
42503 this.markInvalid(String.format(this.minText, this.minValue));
42507 if(num > this.maxValue){
42508 this.markInvalid(String.format(this.maxText, this.maxValue));
42515 validate : function()
42517 if(this.disabled || this.allowBlank){
42522 var currency = this.getCurrency();
42524 if(this.validateValue(this.getRawValue()) && currency.length){
42529 this.markInvalid();
42533 getName: function()
42538 beforeBlur : function()
42544 var v = this.parseValue(this.getRawValue());
42551 onBlur : function()
42555 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42556 //this.el.removeClass(this.focusClass);
42559 this.hasFocus = false;
42561 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42565 var v = this.getValue();
42567 if(String(v) !== String(this.startValue)){
42568 this.fireEvent('change', this, v, this.startValue);
42571 this.fireEvent("blur", this);
42574 inputEl : function()
42576 return this.el.select('.roo-money-amount-input', true).first();
42579 currencyEl : function()
42581 return this.el.select('.roo-money-currency-input', true).first();
42584 hiddenEl : function()
42586 return this.el.select('input.hidden-number-input',true).first();
42590 * @class Roo.bootstrap.BezierSignature
42591 * @extends Roo.bootstrap.Component
42592 * Bootstrap BezierSignature class
42593 * This script refer to:
42594 * Title: Signature Pad
42596 * Availability: https://github.com/szimek/signature_pad
42599 * Create a new BezierSignature
42600 * @param {Object} config The config object
42603 Roo.bootstrap.BezierSignature = function(config){
42604 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42610 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42617 mouse_btn_down: true,
42620 * @cfg {int} canvas height
42622 canvas_height: '200px',
42625 * @cfg {float|function} Radius of a single dot.
42630 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42635 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42640 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42645 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42650 * @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.
42652 bg_color: 'rgba(0, 0, 0, 0)',
42655 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42657 dot_color: 'black',
42660 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42662 velocity_filter_weight: 0.7,
42665 * @cfg {function} Callback when stroke begin.
42670 * @cfg {function} Callback when stroke end.
42674 getAutoCreate : function()
42676 var cls = 'roo-signature column';
42679 cls += ' ' + this.cls;
42689 for(var i = 0; i < col_sizes.length; i++) {
42690 if(this[col_sizes[i]]) {
42691 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42701 cls: 'roo-signature-body',
42705 cls: 'roo-signature-body-canvas',
42706 height: this.canvas_height,
42707 width: this.canvas_width
42714 style: 'display: none'
42722 initEvents: function()
42724 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42726 var canvas = this.canvasEl();
42728 // mouse && touch event swapping...
42729 canvas.dom.style.touchAction = 'none';
42730 canvas.dom.style.msTouchAction = 'none';
42732 this.mouse_btn_down = false;
42733 canvas.on('mousedown', this._handleMouseDown, this);
42734 canvas.on('mousemove', this._handleMouseMove, this);
42735 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42737 if (window.PointerEvent) {
42738 canvas.on('pointerdown', this._handleMouseDown, this);
42739 canvas.on('pointermove', this._handleMouseMove, this);
42740 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42743 if ('ontouchstart' in window) {
42744 canvas.on('touchstart', this._handleTouchStart, this);
42745 canvas.on('touchmove', this._handleTouchMove, this);
42746 canvas.on('touchend', this._handleTouchEnd, this);
42749 Roo.EventManager.onWindowResize(this.resize, this, true);
42751 // file input event
42752 this.fileEl().on('change', this.uploadImage, this);
42759 resize: function(){
42761 var canvas = this.canvasEl().dom;
42762 var ctx = this.canvasElCtx();
42763 var img_data = false;
42765 if(canvas.width > 0) {
42766 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42768 // setting canvas width will clean img data
42771 var style = window.getComputedStyle ?
42772 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42774 var padding_left = parseInt(style.paddingLeft) || 0;
42775 var padding_right = parseInt(style.paddingRight) || 0;
42777 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42780 ctx.putImageData(img_data, 0, 0);
42784 _handleMouseDown: function(e)
42786 if (e.browserEvent.which === 1) {
42787 this.mouse_btn_down = true;
42788 this.strokeBegin(e);
42792 _handleMouseMove: function (e)
42794 if (this.mouse_btn_down) {
42795 this.strokeMoveUpdate(e);
42799 _handleMouseUp: function (e)
42801 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42802 this.mouse_btn_down = false;
42807 _handleTouchStart: function (e) {
42809 e.preventDefault();
42810 if (e.browserEvent.targetTouches.length === 1) {
42811 // var touch = e.browserEvent.changedTouches[0];
42812 // this.strokeBegin(touch);
42814 this.strokeBegin(e); // assume e catching the correct xy...
42818 _handleTouchMove: function (e) {
42819 e.preventDefault();
42820 // var touch = event.targetTouches[0];
42821 // _this._strokeMoveUpdate(touch);
42822 this.strokeMoveUpdate(e);
42825 _handleTouchEnd: function (e) {
42826 var wasCanvasTouched = e.target === this.canvasEl().dom;
42827 if (wasCanvasTouched) {
42828 e.preventDefault();
42829 // var touch = event.changedTouches[0];
42830 // _this._strokeEnd(touch);
42835 reset: function () {
42836 this._lastPoints = [];
42837 this._lastVelocity = 0;
42838 this._lastWidth = (this.min_width + this.max_width) / 2;
42839 this.canvasElCtx().fillStyle = this.dot_color;
42842 strokeMoveUpdate: function(e)
42844 this.strokeUpdate(e);
42846 if (this.throttle) {
42847 this.throttleStroke(this.strokeUpdate, this.throttle);
42850 this.strokeUpdate(e);
42854 strokeBegin: function(e)
42856 var newPointGroup = {
42857 color: this.dot_color,
42861 if (typeof this.onBegin === 'function') {
42865 this.curve_data.push(newPointGroup);
42867 this.strokeUpdate(e);
42870 strokeUpdate: function(e)
42872 var rect = this.canvasEl().dom.getBoundingClientRect();
42873 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42874 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42875 var lastPoints = lastPointGroup.points;
42876 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42877 var isLastPointTooClose = lastPoint
42878 ? point.distanceTo(lastPoint) <= this.min_distance
42880 var color = lastPointGroup.color;
42881 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42882 var curve = this.addPoint(point);
42884 this.drawDot({color: color, point: point});
42887 this.drawCurve({color: color, curve: curve});
42897 strokeEnd: function(e)
42899 this.strokeUpdate(e);
42900 if (typeof this.onEnd === 'function') {
42905 addPoint: function (point) {
42906 var _lastPoints = this._lastPoints;
42907 _lastPoints.push(point);
42908 if (_lastPoints.length > 2) {
42909 if (_lastPoints.length === 3) {
42910 _lastPoints.unshift(_lastPoints[0]);
42912 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42913 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42914 _lastPoints.shift();
42920 calculateCurveWidths: function (startPoint, endPoint) {
42921 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42922 (1 - this.velocity_filter_weight) * this._lastVelocity;
42924 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42927 start: this._lastWidth
42930 this._lastVelocity = velocity;
42931 this._lastWidth = newWidth;
42935 drawDot: function (_a) {
42936 var color = _a.color, point = _a.point;
42937 var ctx = this.canvasElCtx();
42938 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42940 this.drawCurveSegment(point.x, point.y, width);
42942 ctx.fillStyle = color;
42946 drawCurve: function (_a) {
42947 var color = _a.color, curve = _a.curve;
42948 var ctx = this.canvasElCtx();
42949 var widthDelta = curve.endWidth - curve.startWidth;
42950 var drawSteps = Math.floor(curve.length()) * 2;
42952 ctx.fillStyle = color;
42953 for (var i = 0; i < drawSteps; i += 1) {
42954 var t = i / drawSteps;
42960 var x = uuu * curve.startPoint.x;
42961 x += 3 * uu * t * curve.control1.x;
42962 x += 3 * u * tt * curve.control2.x;
42963 x += ttt * curve.endPoint.x;
42964 var y = uuu * curve.startPoint.y;
42965 y += 3 * uu * t * curve.control1.y;
42966 y += 3 * u * tt * curve.control2.y;
42967 y += ttt * curve.endPoint.y;
42968 var width = curve.startWidth + ttt * widthDelta;
42969 this.drawCurveSegment(x, y, width);
42975 drawCurveSegment: function (x, y, width) {
42976 var ctx = this.canvasElCtx();
42978 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42979 this.is_empty = false;
42984 var ctx = this.canvasElCtx();
42985 var canvas = this.canvasEl().dom;
42986 ctx.fillStyle = this.bg_color;
42987 ctx.clearRect(0, 0, canvas.width, canvas.height);
42988 ctx.fillRect(0, 0, canvas.width, canvas.height);
42989 this.curve_data = [];
42991 this.is_empty = true;
42996 return this.el.select('input',true).first();
42999 canvasEl: function()
43001 return this.el.select('canvas',true).first();
43004 canvasElCtx: function()
43006 return this.el.select('canvas',true).first().dom.getContext('2d');
43009 getImage: function(type)
43011 if(this.is_empty) {
43016 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43019 drawFromImage: function(img_src)
43021 var img = new Image();
43023 img.onload = function(){
43024 this.canvasElCtx().drawImage(img, 0, 0);
43029 this.is_empty = false;
43032 selectImage: function()
43034 this.fileEl().dom.click();
43037 uploadImage: function(e)
43039 var reader = new FileReader();
43041 reader.onload = function(e){
43042 var img = new Image();
43043 img.onload = function(){
43045 this.canvasElCtx().drawImage(img, 0, 0);
43047 img.src = e.target.result;
43050 reader.readAsDataURL(e.target.files[0]);
43053 // Bezier Point Constructor
43054 Point: (function () {
43055 function Point(x, y, time) {
43058 this.time = time || Date.now();
43060 Point.prototype.distanceTo = function (start) {
43061 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43063 Point.prototype.equals = function (other) {
43064 return this.x === other.x && this.y === other.y && this.time === other.time;
43066 Point.prototype.velocityFrom = function (start) {
43067 return this.time !== start.time
43068 ? this.distanceTo(start) / (this.time - start.time)
43075 // Bezier Constructor
43076 Bezier: (function () {
43077 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43078 this.startPoint = startPoint;
43079 this.control2 = control2;
43080 this.control1 = control1;
43081 this.endPoint = endPoint;
43082 this.startWidth = startWidth;
43083 this.endWidth = endWidth;
43085 Bezier.fromPoints = function (points, widths, scope) {
43086 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43087 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43088 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43090 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43091 var dx1 = s1.x - s2.x;
43092 var dy1 = s1.y - s2.y;
43093 var dx2 = s2.x - s3.x;
43094 var dy2 = s2.y - s3.y;
43095 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43096 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43097 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43098 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43099 var dxm = m1.x - m2.x;
43100 var dym = m1.y - m2.y;
43101 var k = l2 / (l1 + l2);
43102 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43103 var tx = s2.x - cm.x;
43104 var ty = s2.y - cm.y;
43106 c1: new scope.Point(m1.x + tx, m1.y + ty),
43107 c2: new scope.Point(m2.x + tx, m2.y + ty)
43110 Bezier.prototype.length = function () {
43115 for (var i = 0; i <= steps; i += 1) {
43117 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43118 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43120 var xdiff = cx - px;
43121 var ydiff = cy - py;
43122 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43129 Bezier.prototype.point = function (t, start, c1, c2, end) {
43130 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43131 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43132 + (3.0 * c2 * (1.0 - t) * t * t)
43133 + (end * t * t * t);
43138 throttleStroke: function(fn, wait) {
43139 if (wait === void 0) { wait = 250; }
43141 var timeout = null;
43145 var later = function () {
43146 previous = Date.now();
43148 result = fn.apply(storedContext, storedArgs);
43150 storedContext = null;
43154 return function wrapper() {
43156 for (var _i = 0; _i < arguments.length; _i++) {
43157 args[_i] = arguments[_i];
43159 var now = Date.now();
43160 var remaining = wait - (now - previous);
43161 storedContext = this;
43163 if (remaining <= 0 || remaining > wait) {
43165 clearTimeout(timeout);
43169 result = fn.apply(storedContext, storedArgs);
43171 storedContext = null;
43175 else if (!timeout) {
43176 timeout = window.setTimeout(later, remaining);