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 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 (_self|_blank|_parent|_top|other) target for a href.
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|Boolean} html -- html contents - or just use children.. use false to hide it..
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...
1930 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1931 * @config {Boolean} rotated is the body rotated when rendered...
1934 * Create a new Container
1935 * @param {Object} config The config object
1938 Roo.bootstrap.Card = function(config){
1939 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1945 * When a element a card is dropped
1946 * @param {Roo.bootstrap.Element} this
1947 * @param {Roo.Element} n the node being dropped?
1948 * @param {Object} dd Drag and drop data
1949 * @param {Roo.EventObject} e
1950 * @param {Roo.EventObject} data the data passed via getDragData
1955 * When a element a card is rotate
1956 * @param {Roo.bootstrap.Element} this
1957 * @param {Roo.Element} n the node being dropped?
1958 * @param {Boolean} rotate status
1966 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1971 margin: '', /// may be better in component?
2001 collapsable : false,
2010 childContainer : false,
2011 dropEl : false, /// the dom placeholde element that indicates drop location.
2013 layoutCls : function()
2017 Roo.log(this.margin_bottom.length);
2018 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2019 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2021 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2022 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2024 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2025 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2029 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2030 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2031 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2035 // more generic support?
2043 // Roo.log("Call onRender: " + this.xtype);
2044 /* We are looking at something like this.
2046 <img src="..." class="card-img-top" alt="...">
2047 <div class="card-body">
2048 <h5 class="card-title">Card title</h5>
2049 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2051 >> this bit is really the body...
2052 <div> << we will ad dthis in hopefully it will not break shit.
2054 ** card text does not actually have any styling...
2056 <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>
2059 <a href="#" class="card-link">Card link</a>
2062 <div class="card-footer">
2063 <small class="text-muted">Last updated 3 mins ago</small>
2067 getAutoCreate : function(){
2075 if (this.weight.length && this.weight != 'light') {
2076 cfg.cls += ' text-white';
2078 cfg.cls += ' text-dark'; // need as it's nested..
2080 if (this.weight.length) {
2081 cfg.cls += ' bg-' + this.weight;
2084 cfg.cls += this.layoutCls();
2087 if (this.header.length) {
2089 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2090 cls : 'card-header',
2098 cls : 'card-header d-none',
2103 if (this.collapsable) {
2106 cls : 'd-block user-select-none',
2110 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2115 hdr.cn.push(hdr_ctr);
2120 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2125 if (this.header_image.length) {
2128 cls : 'card-img-top',
2129 src: this.header_image // escape?
2134 cls : 'card-img-top d-none'
2140 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2144 if (this.collapsable || this.rotateable) {
2147 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2154 if (this.title.length) {
2158 src: this.title // escape?
2162 if (this.subtitle.length) {
2166 src: this.subtitle // escape?
2172 cls : 'roo-card-body-ctr'
2175 if (this.html.length) {
2181 // fixme ? handle objects?
2183 if (this.footer.length) {
2186 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2191 cfg.cn.push({cls : 'card-footer d-none'});
2200 getCardHeader : function()
2202 var ret = this.el.select('.card-header',true).first();
2203 if (ret.hasClass('d-none')) {
2204 ret.removeClass('d-none');
2209 getCardFooter : function()
2211 var ret = this.el.select('.card-footer',true).first();
2212 if (ret.hasClass('d-none')) {
2213 ret.removeClass('d-none');
2218 getCardImageTop : function()
2220 var ret = this.el.select('.card-img-top',true).first();
2221 if (ret.hasClass('d-none')) {
2222 ret.removeClass('d-none');
2228 getChildContainer : function()
2234 return this.el.select('.roo-card-body-ctr',true).first();
2237 initEvents: function()
2240 this.bodyEl = this.getChildContainer();
2242 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2243 containerScroll: true,
2244 ddGroup: this.drag_group || 'default_card_drag_group'
2246 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2248 if (this.dropable) {
2249 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2250 containerScroll: true,
2251 ddGroup: this.drop_group || 'default_card_drag_group'
2253 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2254 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2255 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2256 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2257 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2260 if (this.collapsable) {
2261 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2263 if (this.rotateable) {
2264 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2266 this.collapsableEl = this.el.select('.roo-collapsable').first();
2268 this.footerEl = this.el.select('.card-footer').first();
2269 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2270 this.headerEl = this.el.select('.roo-card-header-ctr').first();
2273 this.el.addClass('roo-card-rotated');
2274 this.fireEvent('rotate', this, true);
2278 getDragData : function(e)
2280 var target = this.getEl();
2282 //this.handleSelection(e);
2287 nodes: this.getEl(),
2292 dragData.ddel = target.dom ; // the div element
2293 Roo.log(target.getWidth( ));
2294 dragData.ddel.style.width = target.getWidth() + 'px';
2301 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2302 * whole Element becomes the target, and this causes the drop gesture to append.
2304 getTargetFromEvent : function(e, dragged_card_el)
2306 var target = e.getTarget();
2307 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2308 target = target.parentNode;
2319 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2320 // see if target is one of the 'cards'...
2323 //Roo.log(this.items.length);
2326 var last_card_n = 0;
2328 for (var i = 0;i< this.items.length;i++) {
2330 if (!this.items[i].el.hasClass('card')) {
2333 pos = this.getDropPoint(e, this.items[i].el.dom);
2335 cards_len = ret.cards.length;
2336 //Roo.log(this.items[i].el.dom.id);
2337 ret.cards.push(this.items[i]);
2339 if (ret.card_n < 0 && pos == 'above') {
2340 ret.position = cards_len > 0 ? 'below' : pos;
2341 ret.items_n = i > 0 ? i - 1 : 0;
2342 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2343 ret.card = ret.cards[ret.card_n];
2346 if (!ret.cards.length) {
2348 ret.position = 'below';
2352 // could not find a card.. stick it at the end..
2353 if (ret.card_n < 0) {
2354 ret.card_n = last_card_n;
2355 ret.card = ret.cards[last_card_n];
2356 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2357 ret.position = 'below';
2360 if (this.items[ret.items_n].el == dragged_card_el) {
2364 if (ret.position == 'below') {
2365 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2367 if (card_after && card_after.el == dragged_card_el) {
2374 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2376 if (card_before && card_before.el == dragged_card_el) {
2383 onNodeEnter : function(n, dd, e, data){
2386 onNodeOver : function(n, dd, e, data)
2389 var target_info = this.getTargetFromEvent(e,data.source.el);
2390 if (target_info === false) {
2391 this.dropPlaceHolder('hide');
2394 Roo.log(['getTargetFromEvent', target_info ]);
2397 this.dropPlaceHolder('show', target_info,data);
2401 onNodeOut : function(n, dd, e, data){
2402 this.dropPlaceHolder('hide');
2405 onNodeDrop : function(n, dd, e, data)
2408 // call drop - return false if
2410 // this could actually fail - if the Network drops..
2411 // we will ignore this at present..- client should probably reload
2412 // the whole set of cards if stuff like that fails.
2415 var info = this.getTargetFromEvent(e,data.source.el);
2416 if (info === false) {
2420 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2424 this.dropPlaceHolder('hide');
2426 // do the dom manipulation first..
2427 var dom = data.source.el.dom;
2428 dom.parentNode.removeChild(dom);
2431 if (info.card !== true) {
2432 var cardel = info.card.el.dom;
2434 if (info.position == 'above') {
2435 cardel.parentNode.insertBefore(dom, cardel);
2436 } else if (cardel.nextSibling) {
2437 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2439 cardel.parentNode.append(dom);
2442 // card container???
2443 this.bodyEl.dom.append(dom);
2446 //FIXME HANDLE card = true
2448 // add this to the correct place in items.
2452 // remove Card from items.
2454 var old_parent = data.source.parent();
2456 old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2458 if (this.items.length) {
2460 //Roo.log([info.items_n, info.position, this.items.length]);
2461 for (var i =0; i < this.items.length; i++) {
2462 if (i == info.items_n && info.position == 'above') {
2463 nitems.push(data.source);
2465 nitems.push(this.items[i]);
2466 if (i == info.items_n && info.position == 'below') {
2467 nitems.push(data.source);
2470 this.items = nitems;
2471 Roo.log(this.items);
2473 this.items.push(data.source);
2476 data.source.parentId = this.id;
2481 /** Decide whether to drop above or below a View node. */
2482 getDropPoint : function(e, n, dd)
2487 if (n == this.bodyEl.dom) {
2490 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2491 var c = t + (b - t) / 2;
2492 var y = Roo.lib.Event.getPageY(e);
2499 onToggleCollapse : function(e)
2501 if (this.collapsed) {
2502 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2503 this.collapsableEl.addClass('show');
2504 this.collapsed = false;
2507 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2508 this.collapsableEl.removeClass('show');
2509 this.collapsed = true;
2514 onToggleRotate : function(e)
2516 this.collapsableEl.removeClass('show');
2517 this.footerEl.removeClass('d-none');
2518 this.el.removeClass('roo-card-rotated');
2519 this.el.removeClass('d-none');
2522 this.collapsableEl.addClass('show');
2523 this.rotated = false;
2524 this.fireEvent('rotate', this, this.rotated);
2527 this.el.addClass('roo-card-rotated');
2528 this.footerEl.addClass('d-none');
2529 this.el.select('.roo-collapsable').removeClass('show');
2531 this.rotated = true;
2532 this.fireEvent('rotate', this, this.rotated);
2536 dropPlaceHolder: function (action, info, data)
2538 if (this.dropEl === false) {
2539 this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2543 this.dropEl.removeClass(['d-none', 'd-block']);
2544 if (action == 'hide') {
2546 this.dropEl.addClass('d-none');
2549 // FIXME - info.card == true!!!
2550 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2552 if (info.card !== true) {
2553 var cardel = info.card.el.dom;
2555 if (info.position == 'above') {
2556 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2557 } else if (cardel.nextSibling) {
2558 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2560 cardel.parentNode.append(this.dropEl.dom);
2563 // card container???
2564 this.bodyEl.dom.append(this.dropEl.dom);
2567 this.dropEl.addClass('d-block roo-card-dropzone');
2569 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2576 setHeaderText: function(html)
2578 this.headerEl.dom.innerHTML = html;
2587 * Card header - holder for the card header elements.
2592 * @class Roo.bootstrap.CardHeader
2593 * @extends Roo.bootstrap.Element
2594 * Bootstrap CardHeader class
2596 * Create a new Card Header - that you can embed children into
2597 * @param {Object} config The config object
2600 Roo.bootstrap.CardHeader = function(config){
2601 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2604 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2607 container_method : 'getCardHeader'
2620 * Card footer - holder for the card footer elements.
2625 * @class Roo.bootstrap.CardFooter
2626 * @extends Roo.bootstrap.Element
2627 * Bootstrap CardFooter class
2629 * Create a new Card Footer - that you can embed children into
2630 * @param {Object} config The config object
2633 Roo.bootstrap.CardFooter = function(config){
2634 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2637 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2640 container_method : 'getCardFooter'
2653 * Card header - holder for the card header elements.
2658 * @class Roo.bootstrap.CardImageTop
2659 * @extends Roo.bootstrap.Element
2660 * Bootstrap CardImageTop class
2662 * Create a new Card Image Top container
2663 * @param {Object} config The config object
2666 Roo.bootstrap.CardImageTop = function(config){
2667 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2670 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2673 container_method : 'getCardImageTop'
2691 * @class Roo.bootstrap.Img
2692 * @extends Roo.bootstrap.Component
2693 * Bootstrap Img class
2694 * @cfg {Boolean} imgResponsive false | true
2695 * @cfg {String} border rounded | circle | thumbnail
2696 * @cfg {String} src image source
2697 * @cfg {String} alt image alternative text
2698 * @cfg {String} href a tag href
2699 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2700 * @cfg {String} xsUrl xs image source
2701 * @cfg {String} smUrl sm image source
2702 * @cfg {String} mdUrl md image source
2703 * @cfg {String} lgUrl lg image source
2706 * Create a new Input
2707 * @param {Object} config The config object
2710 Roo.bootstrap.Img = function(config){
2711 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2717 * The img click event for the img.
2718 * @param {Roo.EventObject} e
2724 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2726 imgResponsive: true,
2736 getAutoCreate : function()
2738 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2739 return this.createSingleImg();
2744 cls: 'roo-image-responsive-group',
2749 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2751 if(!_this[size + 'Url']){
2757 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2758 html: _this.html || cfg.html,
2759 src: _this[size + 'Url']
2762 img.cls += ' roo-image-responsive-' + size;
2764 var s = ['xs', 'sm', 'md', 'lg'];
2766 s.splice(s.indexOf(size), 1);
2768 Roo.each(s, function(ss){
2769 img.cls += ' hidden-' + ss;
2772 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2773 cfg.cls += ' img-' + _this.border;
2777 cfg.alt = _this.alt;
2790 a.target = _this.target;
2794 cfg.cn.push((_this.href) ? a : img);
2801 createSingleImg : function()
2805 cls: (this.imgResponsive) ? 'img-responsive' : '',
2807 src : 'about:blank' // just incase src get's set to undefined?!?
2810 cfg.html = this.html || cfg.html;
2812 cfg.src = this.src || cfg.src;
2814 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2815 cfg.cls += ' img-' + this.border;
2832 a.target = this.target;
2837 return (this.href) ? a : cfg;
2840 initEvents: function()
2843 this.el.on('click', this.onClick, this);
2848 onClick : function(e)
2850 Roo.log('img onclick');
2851 this.fireEvent('click', this, e);
2854 * Sets the url of the image - used to update it
2855 * @param {String} url the url of the image
2858 setSrc : function(url)
2862 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2863 this.el.dom.src = url;
2867 this.el.select('img', true).first().dom.src = url;
2883 * @class Roo.bootstrap.Link
2884 * @extends Roo.bootstrap.Component
2885 * Bootstrap Link Class
2886 * @cfg {String} alt image alternative text
2887 * @cfg {String} href a tag href
2888 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2889 * @cfg {String} html the content of the link.
2890 * @cfg {String} anchor name for the anchor link
2891 * @cfg {String} fa - favicon
2893 * @cfg {Boolean} preventDefault (true | false) default false
2897 * Create a new Input
2898 * @param {Object} config The config object
2901 Roo.bootstrap.Link = function(config){
2902 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2908 * The img click event for the img.
2909 * @param {Roo.EventObject} e
2915 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2919 preventDefault: false,
2925 getAutoCreate : function()
2927 var html = this.html || '';
2929 if (this.fa !== false) {
2930 html = '<i class="fa fa-' + this.fa + '"></i>';
2935 // anchor's do not require html/href...
2936 if (this.anchor === false) {
2938 cfg.href = this.href || '#';
2940 cfg.name = this.anchor;
2941 if (this.html !== false || this.fa !== false) {
2944 if (this.href !== false) {
2945 cfg.href = this.href;
2949 if(this.alt !== false){
2954 if(this.target !== false) {
2955 cfg.target = this.target;
2961 initEvents: function() {
2963 if(!this.href || this.preventDefault){
2964 this.el.on('click', this.onClick, this);
2968 onClick : function(e)
2970 if(this.preventDefault){
2973 //Roo.log('img onclick');
2974 this.fireEvent('click', this, e);
2987 * @class Roo.bootstrap.Header
2988 * @extends Roo.bootstrap.Component
2989 * Bootstrap Header class
2990 * @cfg {String} html content of header
2991 * @cfg {Number} level (1|2|3|4|5|6) default 1
2994 * Create a new Header
2995 * @param {Object} config The config object
2999 Roo.bootstrap.Header = function(config){
3000 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3003 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3011 getAutoCreate : function(){
3016 tag: 'h' + (1 *this.level),
3017 html: this.html || ''
3029 * Ext JS Library 1.1.1
3030 * Copyright(c) 2006-2007, Ext JS, LLC.
3032 * Originally Released Under LGPL - original licence link has changed is not relivant.
3035 * <script type="text/javascript">
3039 * @class Roo.bootstrap.MenuMgr
3040 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3043 Roo.bootstrap.MenuMgr = function(){
3044 var menus, active, groups = {}, attached = false, lastShow = new Date();
3046 // private - called when first menu is created
3049 active = new Roo.util.MixedCollection();
3050 Roo.get(document).addKeyListener(27, function(){
3051 if(active.length > 0){
3059 if(active && active.length > 0){
3060 var c = active.clone();
3070 if(active.length < 1){
3071 Roo.get(document).un("mouseup", onMouseDown);
3079 var last = active.last();
3080 lastShow = new Date();
3083 Roo.get(document).on("mouseup", onMouseDown);
3088 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3089 m.parentMenu.activeChild = m;
3090 }else if(last && last.isVisible()){
3091 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3096 function onBeforeHide(m){
3098 m.activeChild.hide();
3100 if(m.autoHideTimer){
3101 clearTimeout(m.autoHideTimer);
3102 delete m.autoHideTimer;
3107 function onBeforeShow(m){
3108 var pm = m.parentMenu;
3109 if(!pm && !m.allowOtherMenus){
3111 }else if(pm && pm.activeChild && active != m){
3112 pm.activeChild.hide();
3116 // private this should really trigger on mouseup..
3117 function onMouseDown(e){
3118 Roo.log("on Mouse Up");
3120 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3121 Roo.log("MenuManager hideAll");
3130 function onBeforeCheck(mi, state){
3132 var g = groups[mi.group];
3133 for(var i = 0, l = g.length; i < l; i++){
3135 g[i].setChecked(false);
3144 * Hides all menus that are currently visible
3146 hideAll : function(){
3151 register : function(menu){
3155 menus[menu.id] = menu;
3156 menu.on("beforehide", onBeforeHide);
3157 menu.on("hide", onHide);
3158 menu.on("beforeshow", onBeforeShow);
3159 menu.on("show", onShow);
3161 if(g && menu.events["checkchange"]){
3165 groups[g].push(menu);
3166 menu.on("checkchange", onCheck);
3171 * Returns a {@link Roo.menu.Menu} object
3172 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3173 * be used to generate and return a new Menu instance.
3175 get : function(menu){
3176 if(typeof menu == "string"){ // menu id
3178 }else if(menu.events){ // menu instance
3181 /*else if(typeof menu.length == 'number'){ // array of menu items?
3182 return new Roo.bootstrap.Menu({items:menu});
3183 }else{ // otherwise, must be a config
3184 return new Roo.bootstrap.Menu(menu);
3191 unregister : function(menu){
3192 delete menus[menu.id];
3193 menu.un("beforehide", onBeforeHide);
3194 menu.un("hide", onHide);
3195 menu.un("beforeshow", onBeforeShow);
3196 menu.un("show", onShow);
3198 if(g && menu.events["checkchange"]){
3199 groups[g].remove(menu);
3200 menu.un("checkchange", onCheck);
3205 registerCheckable : function(menuItem){
3206 var g = menuItem.group;
3211 groups[g].push(menuItem);
3212 menuItem.on("beforecheckchange", onBeforeCheck);
3217 unregisterCheckable : function(menuItem){
3218 var g = menuItem.group;
3220 groups[g].remove(menuItem);
3221 menuItem.un("beforecheckchange", onBeforeCheck);
3233 * @class Roo.bootstrap.Menu
3234 * @extends Roo.bootstrap.Component
3235 * Bootstrap Menu class - container for MenuItems
3236 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3237 * @cfg {bool} hidden if the menu should be hidden when rendered.
3238 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3239 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3243 * @param {Object} config The config object
3247 Roo.bootstrap.Menu = function(config){
3248 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3249 if (this.registerMenu && this.type != 'treeview') {
3250 Roo.bootstrap.MenuMgr.register(this);
3257 * Fires before this menu is displayed (return false to block)
3258 * @param {Roo.menu.Menu} this
3263 * Fires before this menu is hidden (return false to block)
3264 * @param {Roo.menu.Menu} this
3269 * Fires after this menu is displayed
3270 * @param {Roo.menu.Menu} this
3275 * Fires after this menu is hidden
3276 * @param {Roo.menu.Menu} this
3281 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3282 * @param {Roo.menu.Menu} this
3283 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3284 * @param {Roo.EventObject} e
3289 * Fires when the mouse is hovering over this menu
3290 * @param {Roo.menu.Menu} this
3291 * @param {Roo.EventObject} e
3292 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3297 * Fires when the mouse exits this menu
3298 * @param {Roo.menu.Menu} this
3299 * @param {Roo.EventObject} e
3300 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3305 * Fires when a menu item contained in this menu is clicked
3306 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3307 * @param {Roo.EventObject} e
3311 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3314 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3318 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3321 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3323 registerMenu : true,
3325 menuItems :false, // stores the menu items..
3335 getChildContainer : function() {
3339 getAutoCreate : function(){
3341 //if (['right'].indexOf(this.align)!==-1) {
3342 // cfg.cn[1].cls += ' pull-right'
3348 cls : 'dropdown-menu' ,
3349 style : 'z-index:1000'
3353 if (this.type === 'submenu') {
3354 cfg.cls = 'submenu active';
3356 if (this.type === 'treeview') {
3357 cfg.cls = 'treeview-menu';
3362 initEvents : function() {
3364 // Roo.log("ADD event");
3365 // Roo.log(this.triggerEl.dom);
3367 this.triggerEl.on('click', this.onTriggerClick, this);
3369 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3372 if (this.triggerEl.hasClass('nav-item')) {
3373 // dropdown toggle on the 'a' in BS4?
3374 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3376 this.triggerEl.addClass('dropdown-toggle');
3379 this.el.on('touchstart' , this.onTouch, this);
3381 this.el.on('click' , this.onClick, this);
3383 this.el.on("mouseover", this.onMouseOver, this);
3384 this.el.on("mouseout", this.onMouseOut, this);
3388 findTargetItem : function(e)
3390 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3394 //Roo.log(t); Roo.log(t.id);
3396 //Roo.log(this.menuitems);
3397 return this.menuitems.get(t.id);
3399 //return this.items.get(t.menuItemId);
3405 onTouch : function(e)
3407 Roo.log("menu.onTouch");
3408 //e.stopEvent(); this make the user popdown broken
3412 onClick : function(e)
3414 Roo.log("menu.onClick");
3416 var t = this.findTargetItem(e);
3417 if(!t || t.isContainer){
3422 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3423 if(t == this.activeItem && t.shouldDeactivate(e)){
3424 this.activeItem.deactivate();
3425 delete this.activeItem;
3429 this.setActiveItem(t, true);
3437 Roo.log('pass click event');
3441 this.fireEvent("click", this, t, e);
3445 if(!t.href.length || t.href == '#'){
3446 (function() { _this.hide(); }).defer(100);
3451 onMouseOver : function(e){
3452 var t = this.findTargetItem(e);
3455 // if(t.canActivate && !t.disabled){
3456 // this.setActiveItem(t, true);
3460 this.fireEvent("mouseover", this, e, t);
3462 isVisible : function(){
3463 return !this.hidden;
3465 onMouseOut : function(e){
3466 var t = this.findTargetItem(e);
3469 // if(t == this.activeItem && t.shouldDeactivate(e)){
3470 // this.activeItem.deactivate();
3471 // delete this.activeItem;
3474 this.fireEvent("mouseout", this, e, t);
3479 * Displays this menu relative to another element
3480 * @param {String/HTMLElement/Roo.Element} element The element to align to
3481 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3482 * the element (defaults to this.defaultAlign)
3483 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3485 show : function(el, pos, parentMenu)
3487 if (false === this.fireEvent("beforeshow", this)) {
3488 Roo.log("show canceled");
3491 this.parentMenu = parentMenu;
3496 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3499 * Displays this menu at a specific xy position
3500 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3501 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3503 showAt : function(xy, parentMenu, /* private: */_e){
3504 this.parentMenu = parentMenu;
3509 this.fireEvent("beforeshow", this);
3510 //xy = this.el.adjustForConstraints(xy);
3514 this.hideMenuItems();
3515 this.hidden = false;
3516 this.triggerEl.addClass('open');
3517 this.el.addClass('show');
3519 // reassign x when hitting right
3520 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3521 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3524 // reassign y when hitting bottom
3525 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3526 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3529 // but the list may align on trigger left or trigger top... should it be a properity?
3531 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3536 this.fireEvent("show", this);
3542 this.doFocus.defer(50, this);
3546 doFocus : function(){
3548 this.focusEl.focus();
3553 * Hides this menu and optionally all parent menus
3554 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3556 hide : function(deep)
3558 if (false === this.fireEvent("beforehide", this)) {
3559 Roo.log("hide canceled");
3562 this.hideMenuItems();
3563 if(this.el && this.isVisible()){
3565 if(this.activeItem){
3566 this.activeItem.deactivate();
3567 this.activeItem = null;
3569 this.triggerEl.removeClass('open');;
3570 this.el.removeClass('show');
3572 this.fireEvent("hide", this);
3574 if(deep === true && this.parentMenu){
3575 this.parentMenu.hide(true);
3579 onTriggerClick : function(e)
3581 Roo.log('trigger click');
3583 var target = e.getTarget();
3585 Roo.log(target.nodeName.toLowerCase());
3587 if(target.nodeName.toLowerCase() === 'i'){
3593 onTriggerPress : function(e)
3595 Roo.log('trigger press');
3596 //Roo.log(e.getTarget());
3597 // Roo.log(this.triggerEl.dom);
3599 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3600 var pel = Roo.get(e.getTarget());
3601 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3602 Roo.log('is treeview or dropdown?');
3606 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3610 if (this.isVisible()) {
3615 this.show(this.triggerEl, '?', false);
3618 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3625 hideMenuItems : function()
3627 Roo.log("hide Menu Items");
3632 this.el.select('.open',true).each(function(aa) {
3634 aa.removeClass('open');
3638 addxtypeChild : function (tree, cntr) {
3639 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3641 this.menuitems.add(comp);
3653 this.getEl().dom.innerHTML = '';
3654 this.menuitems.clear();
3668 * @class Roo.bootstrap.MenuItem
3669 * @extends Roo.bootstrap.Component
3670 * Bootstrap MenuItem class
3671 * @cfg {String} html the menu label
3672 * @cfg {String} href the link
3673 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3674 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3675 * @cfg {Boolean} active used on sidebars to highlight active itesm
3676 * @cfg {String} fa favicon to show on left of menu item.
3677 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3681 * Create a new MenuItem
3682 * @param {Object} config The config object
3686 Roo.bootstrap.MenuItem = function(config){
3687 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3692 * The raw click event for the entire grid.
3693 * @param {Roo.bootstrap.MenuItem} this
3694 * @param {Roo.EventObject} e
3700 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3704 preventDefault: false,
3705 isContainer : false,
3709 getAutoCreate : function(){
3711 if(this.isContainer){
3714 cls: 'dropdown-menu-item '
3724 cls : 'dropdown-item',
3729 if (this.fa !== false) {
3732 cls : 'fa fa-' + this.fa
3741 cls: 'dropdown-menu-item',
3744 if (this.parent().type == 'treeview') {
3745 cfg.cls = 'treeview-menu';
3748 cfg.cls += ' active';
3753 anc.href = this.href || cfg.cn[0].href ;
3754 ctag.html = this.html || cfg.cn[0].html ;
3758 initEvents: function()
3760 if (this.parent().type == 'treeview') {
3761 this.el.select('a').on('click', this.onClick, this);
3765 this.menu.parentType = this.xtype;
3766 this.menu.triggerEl = this.el;
3767 this.menu = this.addxtype(Roo.apply({}, this.menu));
3771 onClick : function(e)
3773 Roo.log('item on click ');
3775 if(this.preventDefault){
3778 //this.parent().hideMenuItems();
3780 this.fireEvent('click', this, e);
3799 * @class Roo.bootstrap.MenuSeparator
3800 * @extends Roo.bootstrap.Component
3801 * Bootstrap MenuSeparator class
3804 * Create a new MenuItem
3805 * @param {Object} config The config object
3809 Roo.bootstrap.MenuSeparator = function(config){
3810 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3813 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3815 getAutoCreate : function(){
3834 * @class Roo.bootstrap.Modal
3835 * @extends Roo.bootstrap.Component
3836 * Bootstrap Modal class
3837 * @cfg {String} title Title of dialog
3838 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3839 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3840 * @cfg {Boolean} specificTitle default false
3841 * @cfg {Array} buttons Array of buttons or standard button set..
3842 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3843 * @cfg {Boolean} animate default true
3844 * @cfg {Boolean} allow_close default true
3845 * @cfg {Boolean} fitwindow default false
3846 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3847 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3848 * @cfg {String} size (sm|lg) default empty
3849 * @cfg {Number} max_width set the max width of modal
3850 * @cfg {Boolean} editableTitle can the title be edited
3855 * Create a new Modal Dialog
3856 * @param {Object} config The config object
3859 Roo.bootstrap.Modal = function(config){
3860 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3865 * The raw btnclick event for the button
3866 * @param {Roo.EventObject} e
3871 * Fire when dialog resize
3872 * @param {Roo.bootstrap.Modal} this
3873 * @param {Roo.EventObject} e
3877 * @event titlechanged
3878 * Fire when the editable title has been changed
3879 * @param {Roo.bootstrap.Modal} this
3880 * @param {Roo.EventObject} value
3882 "titlechanged" : true
3885 this.buttons = this.buttons || [];
3888 this.tmpl = Roo.factory(this.tmpl);
3893 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3895 title : 'test dialog',
3905 specificTitle: false,
3907 buttonPosition: 'right',
3929 editableTitle : false,
3931 onRender : function(ct, position)
3933 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3936 var cfg = Roo.apply({}, this.getAutoCreate());
3939 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3941 //if (!cfg.name.length) {
3945 cfg.cls += ' ' + this.cls;
3948 cfg.style = this.style;
3950 this.el = Roo.get(document.body).createChild(cfg, position);
3952 //var type = this.el.dom.type;
3955 if(this.tabIndex !== undefined){
3956 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3959 this.dialogEl = this.el.select('.modal-dialog',true).first();
3960 this.bodyEl = this.el.select('.modal-body',true).first();
3961 this.closeEl = this.el.select('.modal-header .close', true).first();
3962 this.headerEl = this.el.select('.modal-header',true).first();
3963 this.titleEl = this.el.select('.modal-title',true).first();
3964 this.footerEl = this.el.select('.modal-footer',true).first();
3966 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3968 //this.el.addClass("x-dlg-modal");
3970 if (this.buttons.length) {
3971 Roo.each(this.buttons, function(bb) {
3972 var b = Roo.apply({}, bb);
3973 b.xns = b.xns || Roo.bootstrap;
3974 b.xtype = b.xtype || 'Button';
3975 if (typeof(b.listeners) == 'undefined') {
3976 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3979 var btn = Roo.factory(b);
3981 btn.render(this.getButtonContainer());
3985 // render the children.
3988 if(typeof(this.items) != 'undefined'){
3989 var items = this.items;
3992 for(var i =0;i < items.length;i++) {
3993 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3997 this.items = nitems;
3999 // where are these used - they used to be body/close/footer
4003 //this.el.addClass([this.fieldClass, this.cls]);
4007 getAutoCreate : function()
4009 // we will default to modal-body-overflow - might need to remove or make optional later.
4011 cls : 'modal-body enable-modal-body-overflow ',
4012 html : this.html || ''
4017 cls : 'modal-title',
4021 if(this.specificTitle){ // WTF is this?
4026 if (this.allow_close && Roo.bootstrap.version == 3) {
4036 if (this.editableTitle) {
4038 cls: 'form-control roo-editable-title d-none',
4044 if (this.allow_close && Roo.bootstrap.version == 4) {
4054 if(this.size.length){
4055 size = 'modal-' + this.size;
4058 var footer = Roo.bootstrap.version == 3 ?
4060 cls : 'modal-footer',
4064 cls: 'btn-' + this.buttonPosition
4069 { // BS4 uses mr-auto on left buttons....
4070 cls : 'modal-footer'
4081 cls: "modal-dialog " + size,
4084 cls : "modal-content",
4087 cls : 'modal-header',
4102 modal.cls += ' fade';
4108 getChildContainer : function() {
4113 getButtonContainer : function() {
4115 return Roo.bootstrap.version == 4 ?
4116 this.el.select('.modal-footer',true).first()
4117 : this.el.select('.modal-footer div',true).first();
4120 initEvents : function()
4122 if (this.allow_close) {
4123 this.closeEl.on('click', this.hide, this);
4125 Roo.EventManager.onWindowResize(this.resize, this, true);
4126 if (this.editableTitle) {
4127 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4128 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4129 this.headerEditEl.on('keyup', function(e) {
4130 if(e.isNavKeyPress()){
4131 this.toggleHeaderInput(false)
4134 this.headerEditEl.on('blur', function(e) {
4135 this.toggleHeaderInput(false)
4144 this.maskEl.setSize(
4145 Roo.lib.Dom.getViewWidth(true),
4146 Roo.lib.Dom.getViewHeight(true)
4149 if (this.fitwindow) {
4153 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4154 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4159 if(this.max_width !== 0) {
4161 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4164 this.setSize(w, this.height);
4168 if(this.max_height) {
4169 this.setSize(w,Math.min(
4171 Roo.lib.Dom.getViewportHeight(true) - 60
4177 if(!this.fit_content) {
4178 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4182 this.setSize(w, Math.min(
4184 this.headerEl.getHeight() +
4185 this.footerEl.getHeight() +
4186 this.getChildHeight(this.bodyEl.dom.childNodes),
4187 Roo.lib.Dom.getViewportHeight(true) - 60)
4193 setSize : function(w,h)
4204 if (!this.rendered) {
4208 //this.el.setStyle('display', 'block');
4209 this.el.removeClass('hideing');
4210 this.el.dom.style.display='block';
4212 Roo.get(document.body).addClass('modal-open');
4214 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4217 this.el.addClass('show');
4218 this.el.addClass('in');
4221 this.el.addClass('show');
4222 this.el.addClass('in');
4225 // not sure how we can show data in here..
4227 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4230 Roo.get(document.body).addClass("x-body-masked");
4232 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4233 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4234 this.maskEl.dom.style.display = 'block';
4235 this.maskEl.addClass('show');
4240 this.fireEvent('show', this);
4242 // set zindex here - otherwise it appears to be ignored...
4243 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4246 this.items.forEach( function(e) {
4247 e.layout ? e.layout() : false;
4255 if(this.fireEvent("beforehide", this) !== false){
4257 this.maskEl.removeClass('show');
4259 this.maskEl.dom.style.display = '';
4260 Roo.get(document.body).removeClass("x-body-masked");
4261 this.el.removeClass('in');
4262 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4264 if(this.animate){ // why
4265 this.el.addClass('hideing');
4266 this.el.removeClass('show');
4268 if (!this.el.hasClass('hideing')) {
4269 return; // it's been shown again...
4272 this.el.dom.style.display='';
4274 Roo.get(document.body).removeClass('modal-open');
4275 this.el.removeClass('hideing');
4279 this.el.removeClass('show');
4280 this.el.dom.style.display='';
4281 Roo.get(document.body).removeClass('modal-open');
4284 this.fireEvent('hide', this);
4287 isVisible : function()
4290 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4294 addButton : function(str, cb)
4298 var b = Roo.apply({}, { html : str } );
4299 b.xns = b.xns || Roo.bootstrap;
4300 b.xtype = b.xtype || 'Button';
4301 if (typeof(b.listeners) == 'undefined') {
4302 b.listeners = { click : cb.createDelegate(this) };
4305 var btn = Roo.factory(b);
4307 btn.render(this.getButtonContainer());
4313 setDefaultButton : function(btn)
4315 //this.el.select('.modal-footer').()
4318 resizeTo: function(w,h)
4320 this.dialogEl.setWidth(w);
4322 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4324 this.bodyEl.setHeight(h - diff);
4326 this.fireEvent('resize', this);
4329 setContentSize : function(w, h)
4333 onButtonClick: function(btn,e)
4336 this.fireEvent('btnclick', btn.name, e);
4339 * Set the title of the Dialog
4340 * @param {String} str new Title
4342 setTitle: function(str) {
4343 this.titleEl.dom.innerHTML = str;
4347 * Set the body of the Dialog
4348 * @param {String} str new Title
4350 setBody: function(str) {
4351 this.bodyEl.dom.innerHTML = str;
4354 * Set the body of the Dialog using the template
4355 * @param {Obj} data - apply this data to the template and replace the body contents.
4357 applyBody: function(obj)
4360 Roo.log("Error - using apply Body without a template");
4363 this.tmpl.overwrite(this.bodyEl, obj);
4366 getChildHeight : function(child_nodes)
4370 child_nodes.length == 0
4375 var child_height = 0;
4377 for(var i = 0; i < child_nodes.length; i++) {
4380 * for modal with tabs...
4381 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4383 var layout_childs = child_nodes[i].childNodes;
4385 for(var j = 0; j < layout_childs.length; j++) {
4387 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4389 var layout_body_childs = layout_childs[j].childNodes;
4391 for(var k = 0; k < layout_body_childs.length; k++) {
4393 if(layout_body_childs[k].classList.contains('navbar')) {
4394 child_height += layout_body_childs[k].offsetHeight;
4398 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4400 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4402 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4404 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4405 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4420 child_height += child_nodes[i].offsetHeight;
4421 // Roo.log(child_nodes[i].offsetHeight);
4424 return child_height;
4426 toggleHeaderInput : function(is_edit)
4429 if (is_edit && this.is_header_editing) {
4430 return; // already editing..
4434 this.headerEditEl.dom.value = this.title;
4435 this.headerEditEl.removeClass('d-none');
4436 this.headerEditEl.dom.focus();
4437 this.titleEl.addClass('d-none');
4439 this.is_header_editing = true;
4442 // flip back to not editing.
4443 this.title = this.headerEditEl.dom.value;
4444 this.headerEditEl.addClass('d-none');
4445 this.titleEl.removeClass('d-none');
4446 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4447 this.is_header_editing = false;
4448 this.fireEvent('titlechanged', this, this.title);
4457 Roo.apply(Roo.bootstrap.Modal, {
4459 * Button config that displays a single OK button
4468 * Button config that displays Yes and No buttons
4484 * Button config that displays OK and Cancel buttons
4499 * Button config that displays Yes, No and Cancel buttons
4524 * messagebox - can be used as a replace
4528 * @class Roo.MessageBox
4529 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4533 Roo.Msg.alert('Status', 'Changes saved successfully.');
4535 // Prompt for user data:
4536 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4538 // process text value...
4542 // Show a dialog using config options:
4544 title:'Save Changes?',
4545 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4546 buttons: Roo.Msg.YESNOCANCEL,
4553 Roo.bootstrap.MessageBox = function(){
4554 var dlg, opt, mask, waitTimer;
4555 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4556 var buttons, activeTextEl, bwidth;
4560 var handleButton = function(button){
4562 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4566 var handleHide = function(){
4568 dlg.el.removeClass(opt.cls);
4571 // Roo.TaskMgr.stop(waitTimer);
4572 // waitTimer = null;
4577 var updateButtons = function(b){
4580 buttons["ok"].hide();
4581 buttons["cancel"].hide();
4582 buttons["yes"].hide();
4583 buttons["no"].hide();
4584 dlg.footerEl.hide();
4588 dlg.footerEl.show();
4589 for(var k in buttons){
4590 if(typeof buttons[k] != "function"){
4593 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4594 width += buttons[k].el.getWidth()+15;
4604 var handleEsc = function(d, k, e){
4605 if(opt && opt.closable !== false){
4615 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4616 * @return {Roo.BasicDialog} The BasicDialog element
4618 getDialog : function(){
4620 dlg = new Roo.bootstrap.Modal( {
4623 //constraintoviewport:false,
4625 //collapsible : false,
4630 //buttonAlign:"center",
4631 closeClick : function(){
4632 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4635 handleButton("cancel");
4640 dlg.on("hide", handleHide);
4642 //dlg.addKeyListener(27, handleEsc);
4644 this.buttons = buttons;
4645 var bt = this.buttonText;
4646 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4647 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4648 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4649 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4651 bodyEl = dlg.bodyEl.createChild({
4653 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4654 '<textarea class="roo-mb-textarea"></textarea>' +
4655 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4657 msgEl = bodyEl.dom.firstChild;
4658 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4659 textboxEl.enableDisplayMode();
4660 textboxEl.addKeyListener([10,13], function(){
4661 if(dlg.isVisible() && opt && opt.buttons){
4664 }else if(opt.buttons.yes){
4665 handleButton("yes");
4669 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4670 textareaEl.enableDisplayMode();
4671 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4672 progressEl.enableDisplayMode();
4674 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4675 var pf = progressEl.dom.firstChild;
4677 pp = Roo.get(pf.firstChild);
4678 pp.setHeight(pf.offsetHeight);
4686 * Updates the message box body text
4687 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4688 * the XHTML-compliant non-breaking space character '&#160;')
4689 * @return {Roo.MessageBox} This message box
4691 updateText : function(text)
4693 if(!dlg.isVisible() && !opt.width){
4694 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4695 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4697 msgEl.innerHTML = text || ' ';
4699 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4700 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4702 Math.min(opt.width || cw , this.maxWidth),
4703 Math.max(opt.minWidth || this.minWidth, bwidth)
4706 activeTextEl.setWidth(w);
4708 if(dlg.isVisible()){
4709 dlg.fixedcenter = false;
4711 // to big, make it scroll. = But as usual stupid IE does not support
4714 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4715 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4716 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4718 bodyEl.dom.style.height = '';
4719 bodyEl.dom.style.overflowY = '';
4722 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4724 bodyEl.dom.style.overflowX = '';
4727 dlg.setContentSize(w, bodyEl.getHeight());
4728 if(dlg.isVisible()){
4729 dlg.fixedcenter = true;
4735 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4736 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4737 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4738 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4739 * @return {Roo.MessageBox} This message box
4741 updateProgress : function(value, text){
4743 this.updateText(text);
4746 if (pp) { // weird bug on my firefox - for some reason this is not defined
4747 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4748 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4754 * Returns true if the message box is currently displayed
4755 * @return {Boolean} True if the message box is visible, else false
4757 isVisible : function(){
4758 return dlg && dlg.isVisible();
4762 * Hides the message box if it is displayed
4765 if(this.isVisible()){
4771 * Displays a new message box, or reinitializes an existing message box, based on the config options
4772 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4773 * The following config object properties are supported:
4775 Property Type Description
4776 ---------- --------------- ------------------------------------------------------------------------------------
4777 animEl String/Element An id or Element from which the message box should animate as it opens and
4778 closes (defaults to undefined)
4779 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4780 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4781 closable Boolean False to hide the top-right close button (defaults to true). Note that
4782 progress and wait dialogs will ignore this property and always hide the
4783 close button as they can only be closed programmatically.
4784 cls String A custom CSS class to apply to the message box element
4785 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4786 displayed (defaults to 75)
4787 fn Function A callback function to execute after closing the dialog. The arguments to the
4788 function will be btn (the name of the button that was clicked, if applicable,
4789 e.g. "ok"), and text (the value of the active text field, if applicable).
4790 Progress and wait dialogs will ignore this option since they do not respond to
4791 user actions and can only be closed programmatically, so any required function
4792 should be called by the same code after it closes the dialog.
4793 icon String A CSS class that provides a background image to be used as an icon for
4794 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4795 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4796 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4797 modal Boolean False to allow user interaction with the page while the message box is
4798 displayed (defaults to true)
4799 msg String A string that will replace the existing message box body text (defaults
4800 to the XHTML-compliant non-breaking space character ' ')
4801 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4802 progress Boolean True to display a progress bar (defaults to false)
4803 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4804 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4805 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4806 title String The title text
4807 value String The string value to set into the active textbox element if displayed
4808 wait Boolean True to display a progress bar (defaults to false)
4809 width Number The width of the dialog in pixels
4816 msg: 'Please enter your address:',
4818 buttons: Roo.MessageBox.OKCANCEL,
4821 animEl: 'addAddressBtn'
4824 * @param {Object} config Configuration options
4825 * @return {Roo.MessageBox} This message box
4827 show : function(options)
4830 // this causes nightmares if you show one dialog after another
4831 // especially on callbacks..
4833 if(this.isVisible()){
4836 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4837 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4838 Roo.log("New Dialog Message:" + options.msg )
4839 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4840 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4843 var d = this.getDialog();
4845 d.setTitle(opt.title || " ");
4846 d.closeEl.setDisplayed(opt.closable !== false);
4847 activeTextEl = textboxEl;
4848 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4853 textareaEl.setHeight(typeof opt.multiline == "number" ?
4854 opt.multiline : this.defaultTextHeight);
4855 activeTextEl = textareaEl;
4864 progressEl.setDisplayed(opt.progress === true);
4866 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4868 this.updateProgress(0);
4869 activeTextEl.dom.value = opt.value || "";
4871 dlg.setDefaultButton(activeTextEl);
4873 var bs = opt.buttons;
4877 }else if(bs && bs.yes){
4878 db = buttons["yes"];
4880 dlg.setDefaultButton(db);
4882 bwidth = updateButtons(opt.buttons);
4883 this.updateText(opt.msg);
4885 d.el.addClass(opt.cls);
4887 d.proxyDrag = opt.proxyDrag === true;
4888 d.modal = opt.modal !== false;
4889 d.mask = opt.modal !== false ? mask : false;
4891 // force it to the end of the z-index stack so it gets a cursor in FF
4892 document.body.appendChild(dlg.el.dom);
4893 d.animateTarget = null;
4894 d.show(options.animEl);
4900 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4901 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4902 * and closing the message box when the process is complete.
4903 * @param {String} title The title bar text
4904 * @param {String} msg The message box body text
4905 * @return {Roo.MessageBox} This message box
4907 progress : function(title, msg){
4914 minWidth: this.minProgressWidth,
4921 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4922 * If a callback function is passed it will be called after the user clicks the button, and the
4923 * id of the button that was clicked will be passed as the only parameter to the callback
4924 * (could also be the top-right close button).
4925 * @param {String} title The title bar text
4926 * @param {String} msg The message box body text
4927 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4928 * @param {Object} scope (optional) The scope of the callback function
4929 * @return {Roo.MessageBox} This message box
4931 alert : function(title, msg, fn, scope)
4946 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4947 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4948 * You are responsible for closing the message box when the process is complete.
4949 * @param {String} msg The message box body text
4950 * @param {String} title (optional) The title bar text
4951 * @return {Roo.MessageBox} This message box
4953 wait : function(msg, title){
4964 waitTimer = Roo.TaskMgr.start({
4966 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4974 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4975 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4976 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4977 * @param {String} title The title bar text
4978 * @param {String} msg The message box body text
4979 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4980 * @param {Object} scope (optional) The scope of the callback function
4981 * @return {Roo.MessageBox} This message box
4983 confirm : function(title, msg, fn, scope){
4987 buttons: this.YESNO,
4996 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4997 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4998 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4999 * (could also be the top-right close button) and the text that was entered will be passed as the two
5000 * parameters to the callback.
5001 * @param {String} title The title bar text
5002 * @param {String} msg The message box body text
5003 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5004 * @param {Object} scope (optional) The scope of the callback function
5005 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5006 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5007 * @return {Roo.MessageBox} This message box
5009 prompt : function(title, msg, fn, scope, multiline){
5013 buttons: this.OKCANCEL,
5018 multiline: multiline,
5025 * Button config that displays a single OK button
5030 * Button config that displays Yes and No buttons
5033 YESNO : {yes:true, no:true},
5035 * Button config that displays OK and Cancel buttons
5038 OKCANCEL : {ok:true, cancel:true},
5040 * Button config that displays Yes, No and Cancel buttons
5043 YESNOCANCEL : {yes:true, no:true, cancel:true},
5046 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5049 defaultTextHeight : 75,
5051 * The maximum width in pixels of the message box (defaults to 600)
5056 * The minimum width in pixels of the message box (defaults to 100)
5061 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5062 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5065 minProgressWidth : 250,
5067 * An object containing the default button text strings that can be overriden for localized language support.
5068 * Supported properties are: ok, cancel, yes and no.
5069 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5082 * Shorthand for {@link Roo.MessageBox}
5084 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5085 Roo.Msg = Roo.Msg || Roo.MessageBox;
5094 * @class Roo.bootstrap.Navbar
5095 * @extends Roo.bootstrap.Component
5096 * Bootstrap Navbar class
5099 * Create a new Navbar
5100 * @param {Object} config The config object
5104 Roo.bootstrap.Navbar = function(config){
5105 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5109 * @event beforetoggle
5110 * Fire before toggle the menu
5111 * @param {Roo.EventObject} e
5113 "beforetoggle" : true
5117 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5126 getAutoCreate : function(){
5129 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5133 initEvents :function ()
5135 //Roo.log(this.el.select('.navbar-toggle',true));
5136 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5143 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5145 var size = this.el.getSize();
5146 this.maskEl.setSize(size.width, size.height);
5147 this.maskEl.enableDisplayMode("block");
5156 getChildContainer : function()
5158 if (this.el && this.el.select('.collapse').getCount()) {
5159 return this.el.select('.collapse',true).first();
5174 onToggle : function()
5177 if(this.fireEvent('beforetoggle', this) === false){
5180 var ce = this.el.select('.navbar-collapse',true).first();
5182 if (!ce.hasClass('show')) {
5192 * Expand the navbar pulldown
5194 expand : function ()
5197 var ce = this.el.select('.navbar-collapse',true).first();
5198 if (ce.hasClass('collapsing')) {
5201 ce.dom.style.height = '';
5203 ce.addClass('in'); // old...
5204 ce.removeClass('collapse');
5205 ce.addClass('show');
5206 var h = ce.getHeight();
5208 ce.removeClass('show');
5209 // at this point we should be able to see it..
5210 ce.addClass('collapsing');
5212 ce.setHeight(0); // resize it ...
5213 ce.on('transitionend', function() {
5214 //Roo.log('done transition');
5215 ce.removeClass('collapsing');
5216 ce.addClass('show');
5217 ce.removeClass('collapse');
5219 ce.dom.style.height = '';
5220 }, this, { single: true} );
5222 ce.dom.scrollTop = 0;
5225 * Collapse the navbar pulldown
5227 collapse : function()
5229 var ce = this.el.select('.navbar-collapse',true).first();
5231 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5232 // it's collapsed or collapsing..
5235 ce.removeClass('in'); // old...
5236 ce.setHeight(ce.getHeight());
5237 ce.removeClass('show');
5238 ce.addClass('collapsing');
5240 ce.on('transitionend', function() {
5241 ce.dom.style.height = '';
5242 ce.removeClass('collapsing');
5243 ce.addClass('collapse');
5244 }, this, { single: true} );
5264 * @class Roo.bootstrap.NavSimplebar
5265 * @extends Roo.bootstrap.Navbar
5266 * Bootstrap Sidebar class
5268 * @cfg {Boolean} inverse is inverted color
5270 * @cfg {String} type (nav | pills | tabs)
5271 * @cfg {Boolean} arrangement stacked | justified
5272 * @cfg {String} align (left | right) alignment
5274 * @cfg {Boolean} main (true|false) main nav bar? default false
5275 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5277 * @cfg {String} tag (header|footer|nav|div) default is nav
5279 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5283 * Create a new Sidebar
5284 * @param {Object} config The config object
5288 Roo.bootstrap.NavSimplebar = function(config){
5289 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5292 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5308 getAutoCreate : function(){
5312 tag : this.tag || 'div',
5313 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5315 if (['light','white'].indexOf(this.weight) > -1) {
5316 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5318 cfg.cls += ' bg-' + this.weight;
5321 cfg.cls += ' navbar-inverse';
5325 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5327 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5336 cls: 'nav nav-' + this.xtype,
5342 this.type = this.type || 'nav';
5343 if (['tabs','pills'].indexOf(this.type) != -1) {
5344 cfg.cn[0].cls += ' nav-' + this.type
5348 if (this.type!=='nav') {
5349 Roo.log('nav type must be nav/tabs/pills')
5351 cfg.cn[0].cls += ' navbar-nav'
5357 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5358 cfg.cn[0].cls += ' nav-' + this.arrangement;
5362 if (this.align === 'right') {
5363 cfg.cn[0].cls += ' navbar-right';
5388 * navbar-expand-md fixed-top
5392 * @class Roo.bootstrap.NavHeaderbar
5393 * @extends Roo.bootstrap.NavSimplebar
5394 * Bootstrap Sidebar class
5396 * @cfg {String} brand what is brand
5397 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5398 * @cfg {String} brand_href href of the brand
5399 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5400 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5401 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5402 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5405 * Create a new Sidebar
5406 * @param {Object} config The config object
5410 Roo.bootstrap.NavHeaderbar = function(config){
5411 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5415 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5422 desktopCenter : false,
5425 getAutoCreate : function(){
5428 tag: this.nav || 'nav',
5429 cls: 'navbar navbar-expand-md',
5435 if (this.desktopCenter) {
5436 cn.push({cls : 'container', cn : []});
5444 cls: 'navbar-toggle navbar-toggler',
5445 'data-toggle': 'collapse',
5450 html: 'Toggle navigation'
5454 cls: 'icon-bar navbar-toggler-icon'
5467 cn.push( Roo.bootstrap.version == 4 ? btn : {
5469 cls: 'navbar-header',
5478 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5482 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5484 if (['light','white'].indexOf(this.weight) > -1) {
5485 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5487 cfg.cls += ' bg-' + this.weight;
5490 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5491 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5493 // tag can override this..
5495 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5498 if (this.brand !== '') {
5499 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5500 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5502 href: this.brand_href ? this.brand_href : '#',
5503 cls: 'navbar-brand',
5511 cfg.cls += ' main-nav';
5519 getHeaderChildContainer : function()
5521 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5522 return this.el.select('.navbar-header',true).first();
5525 return this.getChildContainer();
5528 getChildContainer : function()
5531 return this.el.select('.roo-navbar-collapse',true).first();
5536 initEvents : function()
5538 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5540 if (this.autohide) {
5545 Roo.get(document).on('scroll',function(e) {
5546 var ns = Roo.get(document).getScroll().top;
5547 var os = prevScroll;
5551 ft.removeClass('slideDown');
5552 ft.addClass('slideUp');
5555 ft.removeClass('slideUp');
5556 ft.addClass('slideDown');
5577 * @class Roo.bootstrap.NavSidebar
5578 * @extends Roo.bootstrap.Navbar
5579 * Bootstrap Sidebar class
5582 * Create a new Sidebar
5583 * @param {Object} config The config object
5587 Roo.bootstrap.NavSidebar = function(config){
5588 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5591 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5593 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5595 getAutoCreate : function(){
5600 cls: 'sidebar sidebar-nav'
5622 * @class Roo.bootstrap.NavGroup
5623 * @extends Roo.bootstrap.Component
5624 * Bootstrap NavGroup class
5625 * @cfg {String} align (left|right)
5626 * @cfg {Boolean} inverse
5627 * @cfg {String} type (nav|pills|tab) default nav
5628 * @cfg {String} navId - reference Id for navbar.
5632 * Create a new nav group
5633 * @param {Object} config The config object
5636 Roo.bootstrap.NavGroup = function(config){
5637 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5640 Roo.bootstrap.NavGroup.register(this);
5644 * Fires when the active item changes
5645 * @param {Roo.bootstrap.NavGroup} this
5646 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5647 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5654 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5665 getAutoCreate : function()
5667 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5673 if (Roo.bootstrap.version == 4) {
5674 if (['tabs','pills'].indexOf(this.type) != -1) {
5675 cfg.cls += ' nav-' + this.type;
5677 // trying to remove so header bar can right align top?
5678 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5679 // do not use on header bar...
5680 cfg.cls += ' navbar-nav';
5685 if (['tabs','pills'].indexOf(this.type) != -1) {
5686 cfg.cls += ' nav-' + this.type
5688 if (this.type !== 'nav') {
5689 Roo.log('nav type must be nav/tabs/pills')
5691 cfg.cls += ' navbar-nav'
5695 if (this.parent() && this.parent().sidebar) {
5698 cls: 'dashboard-menu sidebar-menu'
5704 if (this.form === true) {
5707 cls: 'navbar-form form-inline'
5709 //nav navbar-right ml-md-auto
5710 if (this.align === 'right') {
5711 cfg.cls += ' navbar-right ml-md-auto';
5713 cfg.cls += ' navbar-left';
5717 if (this.align === 'right') {
5718 cfg.cls += ' navbar-right ml-md-auto';
5720 cfg.cls += ' mr-auto';
5724 cfg.cls += ' navbar-inverse';
5732 * sets the active Navigation item
5733 * @param {Roo.bootstrap.NavItem} the new current navitem
5735 setActiveItem : function(item)
5738 Roo.each(this.navItems, function(v){
5743 v.setActive(false, true);
5750 item.setActive(true, true);
5751 this.fireEvent('changed', this, item, prev);
5756 * gets the active Navigation item
5757 * @return {Roo.bootstrap.NavItem} the current navitem
5759 getActive : function()
5763 Roo.each(this.navItems, function(v){
5774 indexOfNav : function()
5778 Roo.each(this.navItems, function(v,i){
5789 * adds a Navigation item
5790 * @param {Roo.bootstrap.NavItem} the navitem to add
5792 addItem : function(cfg)
5794 if (this.form && Roo.bootstrap.version == 4) {
5797 var cn = new Roo.bootstrap.NavItem(cfg);
5799 cn.parentId = this.id;
5800 cn.onRender(this.el, null);
5804 * register a Navigation item
5805 * @param {Roo.bootstrap.NavItem} the navitem to add
5807 register : function(item)
5809 this.navItems.push( item);
5810 item.navId = this.navId;
5815 * clear all the Navigation item
5818 clearAll : function()
5821 this.el.dom.innerHTML = '';
5824 getNavItem: function(tabId)
5827 Roo.each(this.navItems, function(e) {
5828 if (e.tabId == tabId) {
5838 setActiveNext : function()
5840 var i = this.indexOfNav(this.getActive());
5841 if (i > this.navItems.length) {
5844 this.setActiveItem(this.navItems[i+1]);
5846 setActivePrev : function()
5848 var i = this.indexOfNav(this.getActive());
5852 this.setActiveItem(this.navItems[i-1]);
5854 clearWasActive : function(except) {
5855 Roo.each(this.navItems, function(e) {
5856 if (e.tabId != except.tabId && e.was_active) {
5857 e.was_active = false;
5864 getWasActive : function ()
5867 Roo.each(this.navItems, function(e) {
5882 Roo.apply(Roo.bootstrap.NavGroup, {
5886 * register a Navigation Group
5887 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5889 register : function(navgrp)
5891 this.groups[navgrp.navId] = navgrp;
5895 * fetch a Navigation Group based on the navigation ID
5896 * @param {string} the navgroup to add
5897 * @returns {Roo.bootstrap.NavGroup} the navgroup
5899 get: function(navId) {
5900 if (typeof(this.groups[navId]) == 'undefined') {
5902 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5904 return this.groups[navId] ;
5919 * @class Roo.bootstrap.NavItem
5920 * @extends Roo.bootstrap.Component
5921 * Bootstrap Navbar.NavItem class
5922 * @cfg {String} href link to
5923 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5925 * @cfg {String} html content of button
5926 * @cfg {String} badge text inside badge
5927 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5928 * @cfg {String} glyphicon DEPRICATED - use fa
5929 * @cfg {String} icon DEPRICATED - use fa
5930 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5931 * @cfg {Boolean} active Is item active
5932 * @cfg {Boolean} disabled Is item disabled
5934 * @cfg {Boolean} preventDefault (true | false) default false
5935 * @cfg {String} tabId the tab that this item activates.
5936 * @cfg {String} tagtype (a|span) render as a href or span?
5937 * @cfg {Boolean} animateRef (true|false) link to element default false
5940 * Create a new Navbar Item
5941 * @param {Object} config The config object
5943 Roo.bootstrap.NavItem = function(config){
5944 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5949 * The raw click event for the entire grid.
5950 * @param {Roo.EventObject} e
5955 * Fires when the active item active state changes
5956 * @param {Roo.bootstrap.NavItem} this
5957 * @param {boolean} state the new state
5963 * Fires when scroll to element
5964 * @param {Roo.bootstrap.NavItem} this
5965 * @param {Object} options
5966 * @param {Roo.EventObject} e
5974 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5983 preventDefault : false,
5991 button_outline : false,
5995 getAutoCreate : function(){
6003 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6005 if (this.disabled) {
6006 cfg.cls += ' disabled';
6010 if (this.button_weight.length) {
6011 cfg.tag = this.href ? 'a' : 'button';
6012 cfg.html = this.html || '';
6013 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6015 cfg.href = this.href;
6018 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6021 // menu .. should add dropdown-menu class - so no need for carat..
6023 if (this.badge !== '') {
6025 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6030 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6034 href : this.href || "#",
6035 html: this.html || ''
6038 if (this.tagtype == 'a') {
6039 cfg.cn[0].cls = 'nav-link';
6042 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6045 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6047 if(this.glyphicon) {
6048 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6053 cfg.cn[0].html += " <span class='caret'></span>";
6057 if (this.badge !== '') {
6059 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6067 onRender : function(ct, position)
6069 // Roo.log("Call onRender: " + this.xtype);
6070 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6074 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6075 this.navLink = this.el.select('.nav-link',true).first();
6080 initEvents: function()
6082 if (typeof (this.menu) != 'undefined') {
6083 this.menu.parentType = this.xtype;
6084 this.menu.triggerEl = this.el;
6085 this.menu = this.addxtype(Roo.apply({}, this.menu));
6088 this.el.select('a',true).on('click', this.onClick, this);
6090 if(this.tagtype == 'span'){
6091 this.el.select('span',true).on('click', this.onClick, this);
6094 // at this point parent should be available..
6095 this.parent().register(this);
6098 onClick : function(e)
6100 if (e.getTarget('.dropdown-menu-item')) {
6101 // did you click on a menu itemm.... - then don't trigger onclick..
6106 this.preventDefault ||
6109 Roo.log("NavItem - prevent Default?");
6113 if (this.disabled) {
6117 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6118 if (tg && tg.transition) {
6119 Roo.log("waiting for the transitionend");
6125 //Roo.log("fire event clicked");
6126 if(this.fireEvent('click', this, e) === false){
6130 if(this.tagtype == 'span'){
6134 //Roo.log(this.href);
6135 var ael = this.el.select('a',true).first();
6138 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6139 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6140 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6141 return; // ignore... - it's a 'hash' to another page.
6143 Roo.log("NavItem - prevent Default?");
6145 this.scrollToElement(e);
6149 var p = this.parent();
6151 if (['tabs','pills'].indexOf(p.type)!==-1) {
6152 if (typeof(p.setActiveItem) !== 'undefined') {
6153 p.setActiveItem(this);
6157 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6158 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6159 // remove the collapsed menu expand...
6160 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6164 isActive: function () {
6167 setActive : function(state, fire, is_was_active)
6169 if (this.active && !state && this.navId) {
6170 this.was_active = true;
6171 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6173 nv.clearWasActive(this);
6177 this.active = state;
6180 this.el.removeClass('active');
6181 this.navLink ? this.navLink.removeClass('active') : false;
6182 } else if (!this.el.hasClass('active')) {
6184 this.el.addClass('active');
6185 if (Roo.bootstrap.version == 4 && this.navLink ) {
6186 this.navLink.addClass('active');
6191 this.fireEvent('changed', this, state);
6194 // show a panel if it's registered and related..
6196 if (!this.navId || !this.tabId || !state || is_was_active) {
6200 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6204 var pan = tg.getPanelByName(this.tabId);
6208 // if we can not flip to new panel - go back to old nav highlight..
6209 if (false == tg.showPanel(pan)) {
6210 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6212 var onav = nv.getWasActive();
6214 onav.setActive(true, false, true);
6223 // this should not be here...
6224 setDisabled : function(state)
6226 this.disabled = state;
6228 this.el.removeClass('disabled');
6229 } else if (!this.el.hasClass('disabled')) {
6230 this.el.addClass('disabled');
6236 * Fetch the element to display the tooltip on.
6237 * @return {Roo.Element} defaults to this.el
6239 tooltipEl : function()
6241 return this.el.select('' + this.tagtype + '', true).first();
6244 scrollToElement : function(e)
6246 var c = document.body;
6249 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6251 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6252 c = document.documentElement;
6255 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6261 var o = target.calcOffsetsTo(c);
6268 this.fireEvent('scrollto', this, options, e);
6270 Roo.get(c).scrollTo('top', options.value, true);
6283 * <span> icon </span>
6284 * <span> text </span>
6285 * <span>badge </span>
6289 * @class Roo.bootstrap.NavSidebarItem
6290 * @extends Roo.bootstrap.NavItem
6291 * Bootstrap Navbar.NavSidebarItem class
6292 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6293 * {Boolean} open is the menu open
6294 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6295 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6296 * {String} buttonSize (sm|md|lg)the extra classes for the button
6297 * {Boolean} showArrow show arrow next to the text (default true)
6299 * Create a new Navbar Button
6300 * @param {Object} config The config object
6302 Roo.bootstrap.NavSidebarItem = function(config){
6303 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6308 * The raw click event for the entire grid.
6309 * @param {Roo.EventObject} e
6314 * Fires when the active item active state changes
6315 * @param {Roo.bootstrap.NavSidebarItem} this
6316 * @param {boolean} state the new state
6324 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6326 badgeWeight : 'default',
6332 buttonWeight : 'default',
6338 getAutoCreate : function(){
6343 href : this.href || '#',
6349 if(this.buttonView){
6352 href : this.href || '#',
6353 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6366 cfg.cls += ' active';
6369 if (this.disabled) {
6370 cfg.cls += ' disabled';
6373 cfg.cls += ' open x-open';
6376 if (this.glyphicon || this.icon) {
6377 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6378 a.cn.push({ tag : 'i', cls : c }) ;
6381 if(!this.buttonView){
6384 html : this.html || ''
6391 if (this.badge !== '') {
6392 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6398 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6401 a.cls += ' dropdown-toggle treeview' ;
6407 initEvents : function()
6409 if (typeof (this.menu) != 'undefined') {
6410 this.menu.parentType = this.xtype;
6411 this.menu.triggerEl = this.el;
6412 this.menu = this.addxtype(Roo.apply({}, this.menu));
6415 this.el.on('click', this.onClick, this);
6417 if(this.badge !== ''){
6418 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6423 onClick : function(e)
6430 if(this.preventDefault){
6434 this.fireEvent('click', this, e);
6437 disable : function()
6439 this.setDisabled(true);
6444 this.setDisabled(false);
6447 setDisabled : function(state)
6449 if(this.disabled == state){
6453 this.disabled = state;
6456 this.el.addClass('disabled');
6460 this.el.removeClass('disabled');
6465 setActive : function(state)
6467 if(this.active == state){
6471 this.active = state;
6474 this.el.addClass('active');
6478 this.el.removeClass('active');
6483 isActive: function ()
6488 setBadge : function(str)
6494 this.badgeEl.dom.innerHTML = str;
6511 * @class Roo.bootstrap.Row
6512 * @extends Roo.bootstrap.Component
6513 * Bootstrap Row class (contains columns...)
6517 * @param {Object} config The config object
6520 Roo.bootstrap.Row = function(config){
6521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6526 getAutoCreate : function(){
6545 * @class Roo.bootstrap.Pagination
6546 * @extends Roo.bootstrap.Component
6547 * Bootstrap Pagination class
6548 * @cfg {String} size xs | sm | md | lg
6549 * @cfg {Boolean} inverse false | true
6552 * Create a new Pagination
6553 * @param {Object} config The config object
6556 Roo.bootstrap.Pagination = function(config){
6557 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6560 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6566 getAutoCreate : function(){
6572 cfg.cls += ' inverse';
6578 cfg.cls += " " + this.cls;
6596 * @class Roo.bootstrap.PaginationItem
6597 * @extends Roo.bootstrap.Component
6598 * Bootstrap PaginationItem class
6599 * @cfg {String} html text
6600 * @cfg {String} href the link
6601 * @cfg {Boolean} preventDefault (true | false) default true
6602 * @cfg {Boolean} active (true | false) default false
6603 * @cfg {Boolean} disabled default false
6607 * Create a new PaginationItem
6608 * @param {Object} config The config object
6612 Roo.bootstrap.PaginationItem = function(config){
6613 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6618 * The raw click event for the entire grid.
6619 * @param {Roo.EventObject} e
6625 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6629 preventDefault: true,
6634 getAutoCreate : function(){
6640 href : this.href ? this.href : '#',
6641 html : this.html ? this.html : ''
6651 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6655 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6661 initEvents: function() {
6663 this.el.on('click', this.onClick, this);
6666 onClick : function(e)
6668 Roo.log('PaginationItem on click ');
6669 if(this.preventDefault){
6677 this.fireEvent('click', this, e);
6693 * @class Roo.bootstrap.Slider
6694 * @extends Roo.bootstrap.Component
6695 * Bootstrap Slider class
6698 * Create a new Slider
6699 * @param {Object} config The config object
6702 Roo.bootstrap.Slider = function(config){
6703 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6706 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6708 getAutoCreate : function(){
6712 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6716 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6728 * Ext JS Library 1.1.1
6729 * Copyright(c) 2006-2007, Ext JS, LLC.
6731 * Originally Released Under LGPL - original licence link has changed is not relivant.
6734 * <script type="text/javascript">
6739 * @class Roo.grid.ColumnModel
6740 * @extends Roo.util.Observable
6741 * This is the default implementation of a ColumnModel used by the Grid. It defines
6742 * the columns in the grid.
6745 var colModel = new Roo.grid.ColumnModel([
6746 {header: "Ticker", width: 60, sortable: true, locked: true},
6747 {header: "Company Name", width: 150, sortable: true},
6748 {header: "Market Cap.", width: 100, sortable: true},
6749 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6750 {header: "Employees", width: 100, sortable: true, resizable: false}
6755 * The config options listed for this class are options which may appear in each
6756 * individual column definition.
6757 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6759 * @param {Object} config An Array of column config objects. See this class's
6760 * config objects for details.
6762 Roo.grid.ColumnModel = function(config){
6764 * The config passed into the constructor
6766 this.config = config;
6769 // if no id, create one
6770 // if the column does not have a dataIndex mapping,
6771 // map it to the order it is in the config
6772 for(var i = 0, len = config.length; i < len; i++){
6774 if(typeof c.dataIndex == "undefined"){
6777 if(typeof c.renderer == "string"){
6778 c.renderer = Roo.util.Format[c.renderer];
6780 if(typeof c.id == "undefined"){
6783 if(c.editor && c.editor.xtype){
6784 c.editor = Roo.factory(c.editor, Roo.grid);
6786 if(c.editor && c.editor.isFormField){
6787 c.editor = new Roo.grid.GridEditor(c.editor);
6789 this.lookup[c.id] = c;
6793 * The width of columns which have no width specified (defaults to 100)
6796 this.defaultWidth = 100;
6799 * Default sortable of columns which have no sortable specified (defaults to false)
6802 this.defaultSortable = false;
6806 * @event widthchange
6807 * Fires when the width of a column changes.
6808 * @param {ColumnModel} this
6809 * @param {Number} columnIndex The column index
6810 * @param {Number} newWidth The new width
6812 "widthchange": true,
6814 * @event headerchange
6815 * Fires when the text of a header changes.
6816 * @param {ColumnModel} this
6817 * @param {Number} columnIndex The column index
6818 * @param {Number} newText The new header text
6820 "headerchange": true,
6822 * @event hiddenchange
6823 * Fires when a column is hidden or "unhidden".
6824 * @param {ColumnModel} this
6825 * @param {Number} columnIndex The column index
6826 * @param {Boolean} hidden true if hidden, false otherwise
6828 "hiddenchange": true,
6830 * @event columnmoved
6831 * Fires when a column is moved.
6832 * @param {ColumnModel} this
6833 * @param {Number} oldIndex
6834 * @param {Number} newIndex
6836 "columnmoved" : true,
6838 * @event columlockchange
6839 * Fires when a column's locked state is changed
6840 * @param {ColumnModel} this
6841 * @param {Number} colIndex
6842 * @param {Boolean} locked true if locked
6844 "columnlockchange" : true
6846 Roo.grid.ColumnModel.superclass.constructor.call(this);
6848 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6850 * @cfg {String} header The header text to display in the Grid view.
6853 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6854 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6855 * specified, the column's index is used as an index into the Record's data Array.
6858 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6859 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6862 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6863 * Defaults to the value of the {@link #defaultSortable} property.
6864 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6867 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6870 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6873 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6876 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6879 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6880 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6881 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6882 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6885 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6888 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6891 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6894 * @cfg {String} cursor (Optional)
6897 * @cfg {String} tooltip (Optional)
6900 * @cfg {Number} xs (Optional)
6903 * @cfg {Number} sm (Optional)
6906 * @cfg {Number} md (Optional)
6909 * @cfg {Number} lg (Optional)
6912 * Returns the id of the column at the specified index.
6913 * @param {Number} index The column index
6914 * @return {String} the id
6916 getColumnId : function(index){
6917 return this.config[index].id;
6921 * Returns the column for a specified id.
6922 * @param {String} id The column id
6923 * @return {Object} the column
6925 getColumnById : function(id){
6926 return this.lookup[id];
6931 * Returns the column for a specified dataIndex.
6932 * @param {String} dataIndex The column dataIndex
6933 * @return {Object|Boolean} the column or false if not found
6935 getColumnByDataIndex: function(dataIndex){
6936 var index = this.findColumnIndex(dataIndex);
6937 return index > -1 ? this.config[index] : false;
6941 * Returns the index for a specified column id.
6942 * @param {String} id The column id
6943 * @return {Number} the index, or -1 if not found
6945 getIndexById : function(id){
6946 for(var i = 0, len = this.config.length; i < len; i++){
6947 if(this.config[i].id == id){
6955 * Returns the index for a specified column dataIndex.
6956 * @param {String} dataIndex The column dataIndex
6957 * @return {Number} the index, or -1 if not found
6960 findColumnIndex : function(dataIndex){
6961 for(var i = 0, len = this.config.length; i < len; i++){
6962 if(this.config[i].dataIndex == dataIndex){
6970 moveColumn : function(oldIndex, newIndex){
6971 var c = this.config[oldIndex];
6972 this.config.splice(oldIndex, 1);
6973 this.config.splice(newIndex, 0, c);
6974 this.dataMap = null;
6975 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6978 isLocked : function(colIndex){
6979 return this.config[colIndex].locked === true;
6982 setLocked : function(colIndex, value, suppressEvent){
6983 if(this.isLocked(colIndex) == value){
6986 this.config[colIndex].locked = value;
6988 this.fireEvent("columnlockchange", this, colIndex, value);
6992 getTotalLockedWidth : function(){
6994 for(var i = 0; i < this.config.length; i++){
6995 if(this.isLocked(i) && !this.isHidden(i)){
6996 this.totalWidth += this.getColumnWidth(i);
7002 getLockedCount : function(){
7003 for(var i = 0, len = this.config.length; i < len; i++){
7004 if(!this.isLocked(i)){
7009 return this.config.length;
7013 * Returns the number of columns.
7016 getColumnCount : function(visibleOnly){
7017 if(visibleOnly === true){
7019 for(var i = 0, len = this.config.length; i < len; i++){
7020 if(!this.isHidden(i)){
7026 return this.config.length;
7030 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7031 * @param {Function} fn
7032 * @param {Object} scope (optional)
7033 * @return {Array} result
7035 getColumnsBy : function(fn, scope){
7037 for(var i = 0, len = this.config.length; i < len; i++){
7038 var c = this.config[i];
7039 if(fn.call(scope||this, c, i) === true){
7047 * Returns true if the specified column is sortable.
7048 * @param {Number} col The column index
7051 isSortable : function(col){
7052 if(typeof this.config[col].sortable == "undefined"){
7053 return this.defaultSortable;
7055 return this.config[col].sortable;
7059 * Returns the rendering (formatting) function defined for the column.
7060 * @param {Number} col The column index.
7061 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7063 getRenderer : function(col){
7064 if(!this.config[col].renderer){
7065 return Roo.grid.ColumnModel.defaultRenderer;
7067 return this.config[col].renderer;
7071 * Sets the rendering (formatting) function for a column.
7072 * @param {Number} col The column index
7073 * @param {Function} fn The function to use to process the cell's raw data
7074 * to return HTML markup for the grid view. The render function is called with
7075 * the following parameters:<ul>
7076 * <li>Data value.</li>
7077 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7078 * <li>css A CSS style string to apply to the table cell.</li>
7079 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7080 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7081 * <li>Row index</li>
7082 * <li>Column index</li>
7083 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7085 setRenderer : function(col, fn){
7086 this.config[col].renderer = fn;
7090 * Returns the width for the specified column.
7091 * @param {Number} col The column index
7094 getColumnWidth : function(col){
7095 return this.config[col].width * 1 || this.defaultWidth;
7099 * Sets the width for a column.
7100 * @param {Number} col The column index
7101 * @param {Number} width The new width
7103 setColumnWidth : function(col, width, suppressEvent){
7104 this.config[col].width = width;
7105 this.totalWidth = null;
7107 this.fireEvent("widthchange", this, col, width);
7112 * Returns the total width of all columns.
7113 * @param {Boolean} includeHidden True to include hidden column widths
7116 getTotalWidth : function(includeHidden){
7117 if(!this.totalWidth){
7118 this.totalWidth = 0;
7119 for(var i = 0, len = this.config.length; i < len; i++){
7120 if(includeHidden || !this.isHidden(i)){
7121 this.totalWidth += this.getColumnWidth(i);
7125 return this.totalWidth;
7129 * Returns the header for the specified column.
7130 * @param {Number} col The column index
7133 getColumnHeader : function(col){
7134 return this.config[col].header;
7138 * Sets the header for a column.
7139 * @param {Number} col The column index
7140 * @param {String} header The new header
7142 setColumnHeader : function(col, header){
7143 this.config[col].header = header;
7144 this.fireEvent("headerchange", this, col, header);
7148 * Returns the tooltip for the specified column.
7149 * @param {Number} col The column index
7152 getColumnTooltip : function(col){
7153 return this.config[col].tooltip;
7156 * Sets the tooltip for a column.
7157 * @param {Number} col The column index
7158 * @param {String} tooltip The new tooltip
7160 setColumnTooltip : function(col, tooltip){
7161 this.config[col].tooltip = tooltip;
7165 * Returns the dataIndex for the specified column.
7166 * @param {Number} col The column index
7169 getDataIndex : function(col){
7170 return this.config[col].dataIndex;
7174 * Sets the dataIndex for a column.
7175 * @param {Number} col The column index
7176 * @param {Number} dataIndex The new dataIndex
7178 setDataIndex : function(col, dataIndex){
7179 this.config[col].dataIndex = dataIndex;
7185 * Returns true if the cell is editable.
7186 * @param {Number} colIndex The column index
7187 * @param {Number} rowIndex The row index - this is nto actually used..?
7190 isCellEditable : function(colIndex, rowIndex){
7191 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7195 * Returns the editor defined for the cell/column.
7196 * return false or null to disable editing.
7197 * @param {Number} colIndex The column index
7198 * @param {Number} rowIndex The row index
7201 getCellEditor : function(colIndex, rowIndex){
7202 return this.config[colIndex].editor;
7206 * Sets if a column is editable.
7207 * @param {Number} col The column index
7208 * @param {Boolean} editable True if the column is editable
7210 setEditable : function(col, editable){
7211 this.config[col].editable = editable;
7216 * Returns true if the column is hidden.
7217 * @param {Number} colIndex The column index
7220 isHidden : function(colIndex){
7221 return this.config[colIndex].hidden;
7226 * Returns true if the column width cannot be changed
7228 isFixed : function(colIndex){
7229 return this.config[colIndex].fixed;
7233 * Returns true if the column can be resized
7236 isResizable : function(colIndex){
7237 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7240 * Sets if a column is hidden.
7241 * @param {Number} colIndex The column index
7242 * @param {Boolean} hidden True if the column is hidden
7244 setHidden : function(colIndex, hidden){
7245 this.config[colIndex].hidden = hidden;
7246 this.totalWidth = null;
7247 this.fireEvent("hiddenchange", this, colIndex, hidden);
7251 * Sets the editor for a column.
7252 * @param {Number} col The column index
7253 * @param {Object} editor The editor object
7255 setEditor : function(col, editor){
7256 this.config[col].editor = editor;
7260 Roo.grid.ColumnModel.defaultRenderer = function(value)
7262 if(typeof value == "object") {
7265 if(typeof value == "string" && value.length < 1){
7269 return String.format("{0}", value);
7272 // Alias for backwards compatibility
7273 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7276 * Ext JS Library 1.1.1
7277 * Copyright(c) 2006-2007, Ext JS, LLC.
7279 * Originally Released Under LGPL - original licence link has changed is not relivant.
7282 * <script type="text/javascript">
7286 * @class Roo.LoadMask
7287 * A simple utility class for generically masking elements while loading data. If the element being masked has
7288 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7289 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7290 * element's UpdateManager load indicator and will be destroyed after the initial load.
7292 * Create a new LoadMask
7293 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7294 * @param {Object} config The config object
7296 Roo.LoadMask = function(el, config){
7297 this.el = Roo.get(el);
7298 Roo.apply(this, config);
7300 this.store.on('beforeload', this.onBeforeLoad, this);
7301 this.store.on('load', this.onLoad, this);
7302 this.store.on('loadexception', this.onLoadException, this);
7303 this.removeMask = false;
7305 var um = this.el.getUpdateManager();
7306 um.showLoadIndicator = false; // disable the default indicator
7307 um.on('beforeupdate', this.onBeforeLoad, this);
7308 um.on('update', this.onLoad, this);
7309 um.on('failure', this.onLoad, this);
7310 this.removeMask = true;
7314 Roo.LoadMask.prototype = {
7316 * @cfg {Boolean} removeMask
7317 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7318 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7322 * The text to display in a centered loading message box (defaults to 'Loading...')
7326 * @cfg {String} msgCls
7327 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7329 msgCls : 'x-mask-loading',
7332 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7338 * Disables the mask to prevent it from being displayed
7340 disable : function(){
7341 this.disabled = true;
7345 * Enables the mask so that it can be displayed
7347 enable : function(){
7348 this.disabled = false;
7351 onLoadException : function()
7355 if (typeof(arguments[3]) != 'undefined') {
7356 Roo.MessageBox.alert("Error loading",arguments[3]);
7360 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7361 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7368 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7373 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7377 onBeforeLoad : function(){
7379 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7384 destroy : function(){
7386 this.store.un('beforeload', this.onBeforeLoad, this);
7387 this.store.un('load', this.onLoad, this);
7388 this.store.un('loadexception', this.onLoadException, this);
7390 var um = this.el.getUpdateManager();
7391 um.un('beforeupdate', this.onBeforeLoad, this);
7392 um.un('update', this.onLoad, this);
7393 um.un('failure', this.onLoad, this);
7404 * @class Roo.bootstrap.Table
7405 * @extends Roo.bootstrap.Component
7406 * Bootstrap Table class
7407 * @cfg {String} cls table class
7408 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7409 * @cfg {String} bgcolor Specifies the background color for a table
7410 * @cfg {Number} border Specifies whether the table cells should have borders or not
7411 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7412 * @cfg {Number} cellspacing Specifies the space between cells
7413 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7414 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7415 * @cfg {String} sortable Specifies that the table should be sortable
7416 * @cfg {String} summary Specifies a summary of the content of a table
7417 * @cfg {Number} width Specifies the width of a table
7418 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7420 * @cfg {boolean} striped Should the rows be alternative striped
7421 * @cfg {boolean} bordered Add borders to the table
7422 * @cfg {boolean} hover Add hover highlighting
7423 * @cfg {boolean} condensed Format condensed
7424 * @cfg {boolean} responsive Format condensed
7425 * @cfg {Boolean} loadMask (true|false) default false
7426 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7427 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7428 * @cfg {Boolean} rowSelection (true|false) default false
7429 * @cfg {Boolean} cellSelection (true|false) default false
7430 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7431 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7432 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7433 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7437 * Create a new Table
7438 * @param {Object} config The config object
7441 Roo.bootstrap.Table = function(config){
7442 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7447 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7448 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7449 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7450 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7452 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7454 this.sm.grid = this;
7455 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7456 this.sm = this.selModel;
7457 this.sm.xmodule = this.xmodule || false;
7460 if (this.cm && typeof(this.cm.config) == 'undefined') {
7461 this.colModel = new Roo.grid.ColumnModel(this.cm);
7462 this.cm = this.colModel;
7463 this.cm.xmodule = this.xmodule || false;
7466 this.store= Roo.factory(this.store, Roo.data);
7467 this.ds = this.store;
7468 this.ds.xmodule = this.xmodule || false;
7471 if (this.footer && this.store) {
7472 this.footer.dataSource = this.ds;
7473 this.footer = Roo.factory(this.footer);
7480 * Fires when a cell is clicked
7481 * @param {Roo.bootstrap.Table} this
7482 * @param {Roo.Element} el
7483 * @param {Number} rowIndex
7484 * @param {Number} columnIndex
7485 * @param {Roo.EventObject} e
7489 * @event celldblclick
7490 * Fires when a cell is double clicked
7491 * @param {Roo.bootstrap.Table} this
7492 * @param {Roo.Element} el
7493 * @param {Number} rowIndex
7494 * @param {Number} columnIndex
7495 * @param {Roo.EventObject} e
7497 "celldblclick" : true,
7500 * Fires when a row is clicked
7501 * @param {Roo.bootstrap.Table} this
7502 * @param {Roo.Element} el
7503 * @param {Number} rowIndex
7504 * @param {Roo.EventObject} e
7508 * @event rowdblclick
7509 * Fires when a row is double clicked
7510 * @param {Roo.bootstrap.Table} this
7511 * @param {Roo.Element} el
7512 * @param {Number} rowIndex
7513 * @param {Roo.EventObject} e
7515 "rowdblclick" : true,
7518 * Fires when a mouseover occur
7519 * @param {Roo.bootstrap.Table} this
7520 * @param {Roo.Element} el
7521 * @param {Number} rowIndex
7522 * @param {Number} columnIndex
7523 * @param {Roo.EventObject} e
7528 * Fires when a mouseout occur
7529 * @param {Roo.bootstrap.Table} this
7530 * @param {Roo.Element} el
7531 * @param {Number} rowIndex
7532 * @param {Number} columnIndex
7533 * @param {Roo.EventObject} e
7538 * Fires when a row is rendered, so you can change add a style to it.
7539 * @param {Roo.bootstrap.Table} this
7540 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7544 * @event rowsrendered
7545 * Fires when all the rows have been rendered
7546 * @param {Roo.bootstrap.Table} this
7548 'rowsrendered' : true,
7550 * @event contextmenu
7551 * The raw contextmenu event for the entire grid.
7552 * @param {Roo.EventObject} e
7554 "contextmenu" : true,
7556 * @event rowcontextmenu
7557 * Fires when a row is right clicked
7558 * @param {Roo.bootstrap.Table} this
7559 * @param {Number} rowIndex
7560 * @param {Roo.EventObject} e
7562 "rowcontextmenu" : true,
7564 * @event cellcontextmenu
7565 * Fires when a cell is right clicked
7566 * @param {Roo.bootstrap.Table} this
7567 * @param {Number} rowIndex
7568 * @param {Number} cellIndex
7569 * @param {Roo.EventObject} e
7571 "cellcontextmenu" : true,
7573 * @event headercontextmenu
7574 * Fires when a header is right clicked
7575 * @param {Roo.bootstrap.Table} this
7576 * @param {Number} columnIndex
7577 * @param {Roo.EventObject} e
7579 "headercontextmenu" : true
7583 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7609 rowSelection : false,
7610 cellSelection : false,
7613 // Roo.Element - the tbody
7615 // Roo.Element - thead element
7618 container: false, // used by gridpanel...
7624 auto_hide_footer : false,
7626 getAutoCreate : function()
7628 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7635 if (this.scrollBody) {
7636 cfg.cls += ' table-body-fixed';
7639 cfg.cls += ' table-striped';
7643 cfg.cls += ' table-hover';
7645 if (this.bordered) {
7646 cfg.cls += ' table-bordered';
7648 if (this.condensed) {
7649 cfg.cls += ' table-condensed';
7651 if (this.responsive) {
7652 cfg.cls += ' table-responsive';
7656 cfg.cls+= ' ' +this.cls;
7659 // this lot should be simplifed...
7672 ].forEach(function(k) {
7680 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7683 if(this.store || this.cm){
7684 if(this.headerShow){
7685 cfg.cn.push(this.renderHeader());
7688 cfg.cn.push(this.renderBody());
7690 if(this.footerShow){
7691 cfg.cn.push(this.renderFooter());
7693 // where does this come from?
7694 //cfg.cls+= ' TableGrid';
7697 return { cn : [ cfg ] };
7700 initEvents : function()
7702 if(!this.store || !this.cm){
7705 if (this.selModel) {
7706 this.selModel.initEvents();
7710 //Roo.log('initEvents with ds!!!!');
7712 this.mainBody = this.el.select('tbody', true).first();
7713 this.mainHead = this.el.select('thead', true).first();
7714 this.mainFoot = this.el.select('tfoot', true).first();
7720 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7721 e.on('click', _this.sort, _this);
7724 this.mainBody.on("click", this.onClick, this);
7725 this.mainBody.on("dblclick", this.onDblClick, this);
7727 // why is this done????? = it breaks dialogs??
7728 //this.parent().el.setStyle('position', 'relative');
7732 this.footer.parentId = this.id;
7733 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7736 this.el.select('tfoot tr td').first().addClass('hide');
7741 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7744 this.store.on('load', this.onLoad, this);
7745 this.store.on('beforeload', this.onBeforeLoad, this);
7746 this.store.on('update', this.onUpdate, this);
7747 this.store.on('add', this.onAdd, this);
7748 this.store.on("clear", this.clear, this);
7750 this.el.on("contextmenu", this.onContextMenu, this);
7752 this.mainBody.on('scroll', this.onBodyScroll, this);
7754 this.cm.on("headerchange", this.onHeaderChange, this);
7756 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7760 onContextMenu : function(e, t)
7762 this.processEvent("contextmenu", e);
7765 processEvent : function(name, e)
7767 if (name != 'touchstart' ) {
7768 this.fireEvent(name, e);
7771 var t = e.getTarget();
7773 var cell = Roo.get(t);
7779 if(cell.findParent('tfoot', false, true)){
7783 if(cell.findParent('thead', false, true)){
7785 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7786 cell = Roo.get(t).findParent('th', false, true);
7788 Roo.log("failed to find th in thead?");
7789 Roo.log(e.getTarget());
7794 var cellIndex = cell.dom.cellIndex;
7796 var ename = name == 'touchstart' ? 'click' : name;
7797 this.fireEvent("header" + ename, this, cellIndex, e);
7802 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7803 cell = Roo.get(t).findParent('td', false, true);
7805 Roo.log("failed to find th in tbody?");
7806 Roo.log(e.getTarget());
7811 var row = cell.findParent('tr', false, true);
7812 var cellIndex = cell.dom.cellIndex;
7813 var rowIndex = row.dom.rowIndex - 1;
7817 this.fireEvent("row" + name, this, rowIndex, e);
7821 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7827 onMouseover : function(e, el)
7829 var cell = Roo.get(el);
7835 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7836 cell = cell.findParent('td', false, true);
7839 var row = cell.findParent('tr', false, true);
7840 var cellIndex = cell.dom.cellIndex;
7841 var rowIndex = row.dom.rowIndex - 1; // start from 0
7843 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7847 onMouseout : function(e, el)
7849 var cell = Roo.get(el);
7855 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7856 cell = cell.findParent('td', false, true);
7859 var row = cell.findParent('tr', false, true);
7860 var cellIndex = cell.dom.cellIndex;
7861 var rowIndex = row.dom.rowIndex - 1; // start from 0
7863 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7867 onClick : function(e, el)
7869 var cell = Roo.get(el);
7871 if(!cell || (!this.cellSelection && !this.rowSelection)){
7875 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7876 cell = cell.findParent('td', false, true);
7879 if(!cell || typeof(cell) == 'undefined'){
7883 var row = cell.findParent('tr', false, true);
7885 if(!row || typeof(row) == 'undefined'){
7889 var cellIndex = cell.dom.cellIndex;
7890 var rowIndex = this.getRowIndex(row);
7892 // why??? - should these not be based on SelectionModel?
7893 if(this.cellSelection){
7894 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7897 if(this.rowSelection){
7898 this.fireEvent('rowclick', this, row, rowIndex, e);
7904 onDblClick : function(e,el)
7906 var cell = Roo.get(el);
7908 if(!cell || (!this.cellSelection && !this.rowSelection)){
7912 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7913 cell = cell.findParent('td', false, true);
7916 if(!cell || typeof(cell) == 'undefined'){
7920 var row = cell.findParent('tr', false, true);
7922 if(!row || typeof(row) == 'undefined'){
7926 var cellIndex = cell.dom.cellIndex;
7927 var rowIndex = this.getRowIndex(row);
7929 if(this.cellSelection){
7930 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7933 if(this.rowSelection){
7934 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7938 sort : function(e,el)
7940 var col = Roo.get(el);
7942 if(!col.hasClass('sortable')){
7946 var sort = col.attr('sort');
7949 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7953 this.store.sortInfo = {field : sort, direction : dir};
7956 Roo.log("calling footer first");
7957 this.footer.onClick('first');
7960 this.store.load({ params : { start : 0 } });
7964 renderHeader : function()
7972 this.totalWidth = 0;
7974 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7976 var config = cm.config[i];
7980 cls : 'x-hcol-' + i,
7982 html: cm.getColumnHeader(i)
7987 if(typeof(config.sortable) != 'undefined' && config.sortable){
7989 c.html = '<i class="glyphicon"></i>' + c.html;
7992 // could use BS4 hidden-..-down
7994 if(typeof(config.lgHeader) != 'undefined'){
7995 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7998 if(typeof(config.mdHeader) != 'undefined'){
7999 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8002 if(typeof(config.smHeader) != 'undefined'){
8003 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8006 if(typeof(config.xsHeader) != 'undefined'){
8007 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8014 if(typeof(config.tooltip) != 'undefined'){
8015 c.tooltip = config.tooltip;
8018 if(typeof(config.colspan) != 'undefined'){
8019 c.colspan = config.colspan;
8022 if(typeof(config.hidden) != 'undefined' && config.hidden){
8023 c.style += ' display:none;';
8026 if(typeof(config.dataIndex) != 'undefined'){
8027 c.sort = config.dataIndex;
8032 if(typeof(config.align) != 'undefined' && config.align.length){
8033 c.style += ' text-align:' + config.align + ';';
8036 if(typeof(config.width) != 'undefined'){
8037 c.style += ' width:' + config.width + 'px;';
8038 this.totalWidth += config.width;
8040 this.totalWidth += 100; // assume minimum of 100 per column?
8043 if(typeof(config.cls) != 'undefined'){
8044 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8047 ['xs','sm','md','lg'].map(function(size){
8049 if(typeof(config[size]) == 'undefined'){
8053 if (!config[size]) { // 0 = hidden
8054 // BS 4 '0' is treated as hide that column and below.
8055 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8059 c.cls += ' col-' + size + '-' + config[size] + (
8060 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8072 renderBody : function()
8082 colspan : this.cm.getColumnCount()
8092 renderFooter : function()
8102 colspan : this.cm.getColumnCount()
8116 // Roo.log('ds onload');
8121 var ds = this.store;
8123 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8124 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8125 if (_this.store.sortInfo) {
8127 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8128 e.select('i', true).addClass(['glyphicon-arrow-up']);
8131 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8132 e.select('i', true).addClass(['glyphicon-arrow-down']);
8137 var tbody = this.mainBody;
8139 if(ds.getCount() > 0){
8140 ds.data.each(function(d,rowIndex){
8141 var row = this.renderRow(cm, ds, rowIndex);
8143 tbody.createChild(row);
8147 if(row.cellObjects.length){
8148 Roo.each(row.cellObjects, function(r){
8149 _this.renderCellObject(r);
8156 var tfoot = this.el.select('tfoot', true).first();
8158 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8160 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8162 var total = this.ds.getTotalCount();
8164 if(this.footer.pageSize < total){
8165 this.mainFoot.show();
8169 Roo.each(this.el.select('tbody td', true).elements, function(e){
8170 e.on('mouseover', _this.onMouseover, _this);
8173 Roo.each(this.el.select('tbody td', true).elements, function(e){
8174 e.on('mouseout', _this.onMouseout, _this);
8176 this.fireEvent('rowsrendered', this);
8182 onUpdate : function(ds,record)
8184 this.refreshRow(record);
8188 onRemove : function(ds, record, index, isUpdate){
8189 if(isUpdate !== true){
8190 this.fireEvent("beforerowremoved", this, index, record);
8192 var bt = this.mainBody.dom;
8194 var rows = this.el.select('tbody > tr', true).elements;
8196 if(typeof(rows[index]) != 'undefined'){
8197 bt.removeChild(rows[index].dom);
8200 // if(bt.rows[index]){
8201 // bt.removeChild(bt.rows[index]);
8204 if(isUpdate !== true){
8205 //this.stripeRows(index);
8206 //this.syncRowHeights(index, index);
8208 this.fireEvent("rowremoved", this, index, record);
8212 onAdd : function(ds, records, rowIndex)
8214 //Roo.log('on Add called');
8215 // - note this does not handle multiple adding very well..
8216 var bt = this.mainBody.dom;
8217 for (var i =0 ; i < records.length;i++) {
8218 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8219 //Roo.log(records[i]);
8220 //Roo.log(this.store.getAt(rowIndex+i));
8221 this.insertRow(this.store, rowIndex + i, false);
8228 refreshRow : function(record){
8229 var ds = this.store, index;
8230 if(typeof record == 'number'){
8232 record = ds.getAt(index);
8234 index = ds.indexOf(record);
8236 this.insertRow(ds, index, true);
8238 this.onRemove(ds, record, index+1, true);
8240 //this.syncRowHeights(index, index);
8242 this.fireEvent("rowupdated", this, index, record);
8245 insertRow : function(dm, rowIndex, isUpdate){
8248 this.fireEvent("beforerowsinserted", this, rowIndex);
8250 //var s = this.getScrollState();
8251 var row = this.renderRow(this.cm, this.store, rowIndex);
8252 // insert before rowIndex..
8253 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8257 if(row.cellObjects.length){
8258 Roo.each(row.cellObjects, function(r){
8259 _this.renderCellObject(r);
8264 this.fireEvent("rowsinserted", this, rowIndex);
8265 //this.syncRowHeights(firstRow, lastRow);
8266 //this.stripeRows(firstRow);
8273 getRowDom : function(rowIndex)
8275 var rows = this.el.select('tbody > tr', true).elements;
8277 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8280 // returns the object tree for a tr..
8283 renderRow : function(cm, ds, rowIndex)
8285 var d = ds.getAt(rowIndex);
8289 cls : 'x-row-' + rowIndex,
8293 var cellObjects = [];
8295 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8296 var config = cm.config[i];
8298 var renderer = cm.getRenderer(i);
8302 if(typeof(renderer) !== 'undefined'){
8303 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8305 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8306 // and are rendered into the cells after the row is rendered - using the id for the element.
8308 if(typeof(value) === 'object'){
8318 rowIndex : rowIndex,
8323 this.fireEvent('rowclass', this, rowcfg);
8327 cls : rowcfg.rowClass + ' x-col-' + i,
8329 html: (typeof(value) === 'object') ? '' : value
8336 if(typeof(config.colspan) != 'undefined'){
8337 td.colspan = config.colspan;
8340 if(typeof(config.hidden) != 'undefined' && config.hidden){
8341 td.style += ' display:none;';
8344 if(typeof(config.align) != 'undefined' && config.align.length){
8345 td.style += ' text-align:' + config.align + ';';
8347 if(typeof(config.valign) != 'undefined' && config.valign.length){
8348 td.style += ' vertical-align:' + config.valign + ';';
8351 if(typeof(config.width) != 'undefined'){
8352 td.style += ' width:' + config.width + 'px;';
8355 if(typeof(config.cursor) != 'undefined'){
8356 td.style += ' cursor:' + config.cursor + ';';
8359 if(typeof(config.cls) != 'undefined'){
8360 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8363 ['xs','sm','md','lg'].map(function(size){
8365 if(typeof(config[size]) == 'undefined'){
8371 if (!config[size]) { // 0 = hidden
8372 // BS 4 '0' is treated as hide that column and below.
8373 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8377 td.cls += ' col-' + size + '-' + config[size] + (
8378 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8388 row.cellObjects = cellObjects;
8396 onBeforeLoad : function()
8405 this.el.select('tbody', true).first().dom.innerHTML = '';
8408 * Show or hide a row.
8409 * @param {Number} rowIndex to show or hide
8410 * @param {Boolean} state hide
8412 setRowVisibility : function(rowIndex, state)
8414 var bt = this.mainBody.dom;
8416 var rows = this.el.select('tbody > tr', true).elements;
8418 if(typeof(rows[rowIndex]) == 'undefined'){
8421 rows[rowIndex].dom.style.display = state ? '' : 'none';
8425 getSelectionModel : function(){
8427 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8429 return this.selModel;
8432 * Render the Roo.bootstrap object from renderder
8434 renderCellObject : function(r)
8438 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8440 var t = r.cfg.render(r.container);
8443 Roo.each(r.cfg.cn, function(c){
8445 container: t.getChildContainer(),
8448 _this.renderCellObject(child);
8453 getRowIndex : function(row)
8457 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8468 * Returns the grid's underlying element = used by panel.Grid
8469 * @return {Element} The element
8471 getGridEl : function(){
8475 * Forces a resize - used by panel.Grid
8476 * @return {Element} The element
8478 autoSize : function()
8480 //var ctr = Roo.get(this.container.dom.parentElement);
8481 var ctr = Roo.get(this.el.dom);
8483 var thd = this.getGridEl().select('thead',true).first();
8484 var tbd = this.getGridEl().select('tbody', true).first();
8485 var tfd = this.getGridEl().select('tfoot', true).first();
8487 var cw = ctr.getWidth();
8491 tbd.setWidth(ctr.getWidth());
8492 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8493 // this needs fixing for various usage - currently only hydra job advers I think..
8495 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8497 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8500 cw = Math.max(cw, this.totalWidth);
8501 this.getGridEl().select('tr',true).setWidth(cw);
8502 // resize 'expandable coloumn?
8504 return; // we doe not have a view in this design..
8507 onBodyScroll: function()
8509 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8511 this.mainHead.setStyle({
8512 'position' : 'relative',
8513 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8519 var scrollHeight = this.mainBody.dom.scrollHeight;
8521 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8523 var height = this.mainBody.getHeight();
8525 if(scrollHeight - height == scrollTop) {
8527 var total = this.ds.getTotalCount();
8529 if(this.footer.cursor + this.footer.pageSize < total){
8531 this.footer.ds.load({
8533 start : this.footer.cursor + this.footer.pageSize,
8534 limit : this.footer.pageSize
8544 onHeaderChange : function()
8546 var header = this.renderHeader();
8547 var table = this.el.select('table', true).first();
8549 this.mainHead.remove();
8550 this.mainHead = table.createChild(header, this.mainBody, false);
8553 onHiddenChange : function(colModel, colIndex, hidden)
8555 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8556 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8558 this.CSS.updateRule(thSelector, "display", "");
8559 this.CSS.updateRule(tdSelector, "display", "");
8562 this.CSS.updateRule(thSelector, "display", "none");
8563 this.CSS.updateRule(tdSelector, "display", "none");
8566 this.onHeaderChange();
8570 setColumnWidth: function(col_index, width)
8572 // width = "md-2 xs-2..."
8573 if(!this.colModel.config[col_index]) {
8577 var w = width.split(" ");
8579 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8581 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8584 for(var j = 0; j < w.length; j++) {
8590 var size_cls = w[j].split("-");
8592 if(!Number.isInteger(size_cls[1] * 1)) {
8596 if(!this.colModel.config[col_index][size_cls[0]]) {
8600 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8604 h_row[0].classList.replace(
8605 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8606 "col-"+size_cls[0]+"-"+size_cls[1]
8609 for(var i = 0; i < rows.length; i++) {
8611 var size_cls = w[j].split("-");
8613 if(!Number.isInteger(size_cls[1] * 1)) {
8617 if(!this.colModel.config[col_index][size_cls[0]]) {
8621 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8625 rows[i].classList.replace(
8626 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8627 "col-"+size_cls[0]+"-"+size_cls[1]
8631 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8646 * @class Roo.bootstrap.TableCell
8647 * @extends Roo.bootstrap.Component
8648 * Bootstrap TableCell class
8649 * @cfg {String} html cell contain text
8650 * @cfg {String} cls cell class
8651 * @cfg {String} tag cell tag (td|th) default td
8652 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8653 * @cfg {String} align Aligns the content in a cell
8654 * @cfg {String} axis Categorizes cells
8655 * @cfg {String} bgcolor Specifies the background color of a cell
8656 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8657 * @cfg {Number} colspan Specifies the number of columns a cell should span
8658 * @cfg {String} headers Specifies one or more header cells a cell is related to
8659 * @cfg {Number} height Sets the height of a cell
8660 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8661 * @cfg {Number} rowspan Sets the number of rows a cell should span
8662 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8663 * @cfg {String} valign Vertical aligns the content in a cell
8664 * @cfg {Number} width Specifies the width of a cell
8667 * Create a new TableCell
8668 * @param {Object} config The config object
8671 Roo.bootstrap.TableCell = function(config){
8672 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8675 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8695 getAutoCreate : function(){
8696 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8716 cfg.align=this.align
8722 cfg.bgcolor=this.bgcolor
8725 cfg.charoff=this.charoff
8728 cfg.colspan=this.colspan
8731 cfg.headers=this.headers
8734 cfg.height=this.height
8737 cfg.nowrap=this.nowrap
8740 cfg.rowspan=this.rowspan
8743 cfg.scope=this.scope
8746 cfg.valign=this.valign
8749 cfg.width=this.width
8768 * @class Roo.bootstrap.TableRow
8769 * @extends Roo.bootstrap.Component
8770 * Bootstrap TableRow class
8771 * @cfg {String} cls row class
8772 * @cfg {String} align Aligns the content in a table row
8773 * @cfg {String} bgcolor Specifies a background color for a table row
8774 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8775 * @cfg {String} valign Vertical aligns the content in a table row
8778 * Create a new TableRow
8779 * @param {Object} config The config object
8782 Roo.bootstrap.TableRow = function(config){
8783 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8786 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8794 getAutoCreate : function(){
8795 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8805 cfg.align = this.align;
8808 cfg.bgcolor = this.bgcolor;
8811 cfg.charoff = this.charoff;
8814 cfg.valign = this.valign;
8832 * @class Roo.bootstrap.TableBody
8833 * @extends Roo.bootstrap.Component
8834 * Bootstrap TableBody class
8835 * @cfg {String} cls element class
8836 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8837 * @cfg {String} align Aligns the content inside the element
8838 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8839 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8842 * Create a new TableBody
8843 * @param {Object} config The config object
8846 Roo.bootstrap.TableBody = function(config){
8847 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8850 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8858 getAutoCreate : function(){
8859 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8873 cfg.align = this.align;
8876 cfg.charoff = this.charoff;
8879 cfg.valign = this.valign;
8886 // initEvents : function()
8893 // this.store = Roo.factory(this.store, Roo.data);
8894 // this.store.on('load', this.onLoad, this);
8896 // this.store.load();
8900 // onLoad: function ()
8902 // this.fireEvent('load', this);
8912 * Ext JS Library 1.1.1
8913 * Copyright(c) 2006-2007, Ext JS, LLC.
8915 * Originally Released Under LGPL - original licence link has changed is not relivant.
8918 * <script type="text/javascript">
8921 // as we use this in bootstrap.
8922 Roo.namespace('Roo.form');
8924 * @class Roo.form.Action
8925 * Internal Class used to handle form actions
8927 * @param {Roo.form.BasicForm} el The form element or its id
8928 * @param {Object} config Configuration options
8933 // define the action interface
8934 Roo.form.Action = function(form, options){
8936 this.options = options || {};
8939 * Client Validation Failed
8942 Roo.form.Action.CLIENT_INVALID = 'client';
8944 * Server Validation Failed
8947 Roo.form.Action.SERVER_INVALID = 'server';
8949 * Connect to Server Failed
8952 Roo.form.Action.CONNECT_FAILURE = 'connect';
8954 * Reading Data from Server Failed
8957 Roo.form.Action.LOAD_FAILURE = 'load';
8959 Roo.form.Action.prototype = {
8961 failureType : undefined,
8962 response : undefined,
8966 run : function(options){
8971 success : function(response){
8976 handleResponse : function(response){
8980 // default connection failure
8981 failure : function(response){
8983 this.response = response;
8984 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8985 this.form.afterAction(this, false);
8988 processResponse : function(response){
8989 this.response = response;
8990 if(!response.responseText){
8993 this.result = this.handleResponse(response);
8997 // utility functions used internally
8998 getUrl : function(appendParams){
8999 var url = this.options.url || this.form.url || this.form.el.dom.action;
9001 var p = this.getParams();
9003 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9009 getMethod : function(){
9010 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9013 getParams : function(){
9014 var bp = this.form.baseParams;
9015 var p = this.options.params;
9017 if(typeof p == "object"){
9018 p = Roo.urlEncode(Roo.applyIf(p, bp));
9019 }else if(typeof p == 'string' && bp){
9020 p += '&' + Roo.urlEncode(bp);
9023 p = Roo.urlEncode(bp);
9028 createCallback : function(){
9030 success: this.success,
9031 failure: this.failure,
9033 timeout: (this.form.timeout*1000),
9034 upload: this.form.fileUpload ? this.success : undefined
9039 Roo.form.Action.Submit = function(form, options){
9040 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9043 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9046 haveProgress : false,
9047 uploadComplete : false,
9049 // uploadProgress indicator.
9050 uploadProgress : function()
9052 if (!this.form.progressUrl) {
9056 if (!this.haveProgress) {
9057 Roo.MessageBox.progress("Uploading", "Uploading");
9059 if (this.uploadComplete) {
9060 Roo.MessageBox.hide();
9064 this.haveProgress = true;
9066 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9068 var c = new Roo.data.Connection();
9070 url : this.form.progressUrl,
9075 success : function(req){
9076 //console.log(data);
9080 rdata = Roo.decode(req.responseText)
9082 Roo.log("Invalid data from server..");
9086 if (!rdata || !rdata.success) {
9088 Roo.MessageBox.alert(Roo.encode(rdata));
9091 var data = rdata.data;
9093 if (this.uploadComplete) {
9094 Roo.MessageBox.hide();
9099 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9100 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9103 this.uploadProgress.defer(2000,this);
9106 failure: function(data) {
9107 Roo.log('progress url failed ');
9118 // run get Values on the form, so it syncs any secondary forms.
9119 this.form.getValues();
9121 var o = this.options;
9122 var method = this.getMethod();
9123 var isPost = method == 'POST';
9124 if(o.clientValidation === false || this.form.isValid()){
9126 if (this.form.progressUrl) {
9127 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9128 (new Date() * 1) + '' + Math.random());
9133 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9134 form:this.form.el.dom,
9135 url:this.getUrl(!isPost),
9137 params:isPost ? this.getParams() : null,
9138 isUpload: this.form.fileUpload,
9139 formData : this.form.formData
9142 this.uploadProgress();
9144 }else if (o.clientValidation !== false){ // client validation failed
9145 this.failureType = Roo.form.Action.CLIENT_INVALID;
9146 this.form.afterAction(this, false);
9150 success : function(response)
9152 this.uploadComplete= true;
9153 if (this.haveProgress) {
9154 Roo.MessageBox.hide();
9158 var result = this.processResponse(response);
9159 if(result === true || result.success){
9160 this.form.afterAction(this, true);
9164 this.form.markInvalid(result.errors);
9165 this.failureType = Roo.form.Action.SERVER_INVALID;
9167 this.form.afterAction(this, false);
9169 failure : function(response)
9171 this.uploadComplete= true;
9172 if (this.haveProgress) {
9173 Roo.MessageBox.hide();
9176 this.response = response;
9177 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9178 this.form.afterAction(this, false);
9181 handleResponse : function(response){
9182 if(this.form.errorReader){
9183 var rs = this.form.errorReader.read(response);
9186 for(var i = 0, len = rs.records.length; i < len; i++) {
9187 var r = rs.records[i];
9191 if(errors.length < 1){
9195 success : rs.success,
9201 ret = Roo.decode(response.responseText);
9205 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9215 Roo.form.Action.Load = function(form, options){
9216 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9217 this.reader = this.form.reader;
9220 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9225 Roo.Ajax.request(Roo.apply(
9226 this.createCallback(), {
9227 method:this.getMethod(),
9228 url:this.getUrl(false),
9229 params:this.getParams()
9233 success : function(response){
9235 var result = this.processResponse(response);
9236 if(result === true || !result.success || !result.data){
9237 this.failureType = Roo.form.Action.LOAD_FAILURE;
9238 this.form.afterAction(this, false);
9241 this.form.clearInvalid();
9242 this.form.setValues(result.data);
9243 this.form.afterAction(this, true);
9246 handleResponse : function(response){
9247 if(this.form.reader){
9248 var rs = this.form.reader.read(response);
9249 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9251 success : rs.success,
9255 return Roo.decode(response.responseText);
9259 Roo.form.Action.ACTION_TYPES = {
9260 'load' : Roo.form.Action.Load,
9261 'submit' : Roo.form.Action.Submit
9270 * @class Roo.bootstrap.Form
9271 * @extends Roo.bootstrap.Component
9272 * Bootstrap Form class
9273 * @cfg {String} method GET | POST (default POST)
9274 * @cfg {String} labelAlign top | left (default top)
9275 * @cfg {String} align left | right - for navbars
9276 * @cfg {Boolean} loadMask load mask when submit (default true)
9281 * @param {Object} config The config object
9285 Roo.bootstrap.Form = function(config){
9287 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9289 Roo.bootstrap.Form.popover.apply();
9293 * @event clientvalidation
9294 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9295 * @param {Form} this
9296 * @param {Boolean} valid true if the form has passed client-side validation
9298 clientvalidation: true,
9300 * @event beforeaction
9301 * Fires before any action is performed. Return false to cancel the action.
9302 * @param {Form} this
9303 * @param {Action} action The action to be performed
9307 * @event actionfailed
9308 * Fires when an action fails.
9309 * @param {Form} this
9310 * @param {Action} action The action that failed
9312 actionfailed : true,
9314 * @event actioncomplete
9315 * Fires when an action is completed.
9316 * @param {Form} this
9317 * @param {Action} action The action that completed
9319 actioncomplete : true
9323 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9326 * @cfg {String} method
9327 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9332 * The URL to use for form actions if one isn't supplied in the action options.
9335 * @cfg {Boolean} fileUpload
9336 * Set to true if this form is a file upload.
9340 * @cfg {Object} baseParams
9341 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9345 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9349 * @cfg {Sting} align (left|right) for navbar forms
9354 activeAction : null,
9357 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9358 * element by passing it or its id or mask the form itself by passing in true.
9361 waitMsgTarget : false,
9366 * @cfg {Boolean} errorMask (true|false) default false
9371 * @cfg {Number} maskOffset Default 100
9376 * @cfg {Boolean} maskBody
9380 getAutoCreate : function(){
9384 method : this.method || 'POST',
9385 id : this.id || Roo.id(),
9388 if (this.parent().xtype.match(/^Nav/)) {
9389 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9393 if (this.labelAlign == 'left' ) {
9394 cfg.cls += ' form-horizontal';
9400 initEvents : function()
9402 this.el.on('submit', this.onSubmit, this);
9403 // this was added as random key presses on the form where triggering form submit.
9404 this.el.on('keypress', function(e) {
9405 if (e.getCharCode() != 13) {
9408 // we might need to allow it for textareas.. and some other items.
9409 // check e.getTarget().
9411 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9415 Roo.log("keypress blocked");
9423 onSubmit : function(e){
9428 * Returns true if client-side validation on the form is successful.
9431 isValid : function(){
9432 var items = this.getItems();
9436 items.each(function(f){
9442 Roo.log('invalid field: ' + f.name);
9446 if(!target && f.el.isVisible(true)){
9452 if(this.errorMask && !valid){
9453 Roo.bootstrap.Form.popover.mask(this, target);
9460 * Returns true if any fields in this form have changed since their original load.
9463 isDirty : function(){
9465 var items = this.getItems();
9466 items.each(function(f){
9476 * Performs a predefined action (submit or load) or custom actions you define on this form.
9477 * @param {String} actionName The name of the action type
9478 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9479 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9480 * accept other config options):
9482 Property Type Description
9483 ---------------- --------------- ----------------------------------------------------------------------------------
9484 url String The url for the action (defaults to the form's url)
9485 method String The form method to use (defaults to the form's method, or POST if not defined)
9486 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9487 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9488 validate the form on the client (defaults to false)
9490 * @return {BasicForm} this
9492 doAction : function(action, options){
9493 if(typeof action == 'string'){
9494 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9496 if(this.fireEvent('beforeaction', this, action) !== false){
9497 this.beforeAction(action);
9498 action.run.defer(100, action);
9504 beforeAction : function(action){
9505 var o = action.options;
9510 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9512 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9515 // not really supported yet.. ??
9517 //if(this.waitMsgTarget === true){
9518 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9519 //}else if(this.waitMsgTarget){
9520 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9521 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9523 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9529 afterAction : function(action, success){
9530 this.activeAction = null;
9531 var o = action.options;
9536 Roo.get(document.body).unmask();
9542 //if(this.waitMsgTarget === true){
9543 // this.el.unmask();
9544 //}else if(this.waitMsgTarget){
9545 // this.waitMsgTarget.unmask();
9547 // Roo.MessageBox.updateProgress(1);
9548 // Roo.MessageBox.hide();
9555 Roo.callback(o.success, o.scope, [this, action]);
9556 this.fireEvent('actioncomplete', this, action);
9560 // failure condition..
9561 // we have a scenario where updates need confirming.
9562 // eg. if a locking scenario exists..
9563 // we look for { errors : { needs_confirm : true }} in the response.
9565 (typeof(action.result) != 'undefined') &&
9566 (typeof(action.result.errors) != 'undefined') &&
9567 (typeof(action.result.errors.needs_confirm) != 'undefined')
9570 Roo.log("not supported yet");
9573 Roo.MessageBox.confirm(
9574 "Change requires confirmation",
9575 action.result.errorMsg,
9580 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9590 Roo.callback(o.failure, o.scope, [this, action]);
9591 // show an error message if no failed handler is set..
9592 if (!this.hasListener('actionfailed')) {
9593 Roo.log("need to add dialog support");
9595 Roo.MessageBox.alert("Error",
9596 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9597 action.result.errorMsg :
9598 "Saving Failed, please check your entries or try again"
9603 this.fireEvent('actionfailed', this, action);
9608 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9609 * @param {String} id The value to search for
9612 findField : function(id){
9613 var items = this.getItems();
9614 var field = items.get(id);
9616 items.each(function(f){
9617 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9624 return field || null;
9627 * Mark fields in this form invalid in bulk.
9628 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9629 * @return {BasicForm} this
9631 markInvalid : function(errors){
9632 if(errors instanceof Array){
9633 for(var i = 0, len = errors.length; i < len; i++){
9634 var fieldError = errors[i];
9635 var f = this.findField(fieldError.id);
9637 f.markInvalid(fieldError.msg);
9643 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9644 field.markInvalid(errors[id]);
9648 //Roo.each(this.childForms || [], function (f) {
9649 // f.markInvalid(errors);
9656 * Set values for fields in this form in bulk.
9657 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9658 * @return {BasicForm} this
9660 setValues : function(values){
9661 if(values instanceof Array){ // array of objects
9662 for(var i = 0, len = values.length; i < len; i++){
9664 var f = this.findField(v.id);
9666 f.setValue(v.value);
9667 if(this.trackResetOnLoad){
9668 f.originalValue = f.getValue();
9672 }else{ // object hash
9675 if(typeof values[id] != 'function' && (field = this.findField(id))){
9677 if (field.setFromData &&
9679 field.displayField &&
9680 // combos' with local stores can
9681 // be queried via setValue()
9682 // to set their value..
9683 (field.store && !field.store.isLocal)
9687 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9688 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9689 field.setFromData(sd);
9691 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9693 field.setFromData(values);
9696 field.setValue(values[id]);
9700 if(this.trackResetOnLoad){
9701 field.originalValue = field.getValue();
9707 //Roo.each(this.childForms || [], function (f) {
9708 // f.setValues(values);
9715 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9716 * they are returned as an array.
9717 * @param {Boolean} asString
9720 getValues : function(asString){
9721 //if (this.childForms) {
9722 // copy values from the child forms
9723 // Roo.each(this.childForms, function (f) {
9724 // this.setValues(f.getValues());
9730 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9731 if(asString === true){
9734 return Roo.urlDecode(fs);
9738 * Returns the fields in this form as an object with key/value pairs.
9739 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9742 getFieldValues : function(with_hidden)
9744 var items = this.getItems();
9746 items.each(function(f){
9752 var v = f.getValue();
9754 if (f.inputType =='radio') {
9755 if (typeof(ret[f.getName()]) == 'undefined') {
9756 ret[f.getName()] = ''; // empty..
9759 if (!f.el.dom.checked) {
9767 if(f.xtype == 'MoneyField'){
9768 ret[f.currencyName] = f.getCurrency();
9771 // not sure if this supported any more..
9772 if ((typeof(v) == 'object') && f.getRawValue) {
9773 v = f.getRawValue() ; // dates..
9775 // combo boxes where name != hiddenName...
9776 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9777 ret[f.name] = f.getRawValue();
9779 ret[f.getName()] = v;
9786 * Clears all invalid messages in this form.
9787 * @return {BasicForm} this
9789 clearInvalid : function(){
9790 var items = this.getItems();
9792 items.each(function(f){
9801 * @return {BasicForm} this
9804 var items = this.getItems();
9805 items.each(function(f){
9809 Roo.each(this.childForms || [], function (f) {
9817 getItems : function()
9819 var r=new Roo.util.MixedCollection(false, function(o){
9820 return o.id || (o.id = Roo.id());
9822 var iter = function(el) {
9829 Roo.each(el.items,function(e) {
9838 hideFields : function(items)
9840 Roo.each(items, function(i){
9842 var f = this.findField(i);
9853 showFields : function(items)
9855 Roo.each(items, function(i){
9857 var f = this.findField(i);
9870 Roo.apply(Roo.bootstrap.Form, {
9897 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9898 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9899 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9900 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9903 this.maskEl.top.enableDisplayMode("block");
9904 this.maskEl.left.enableDisplayMode("block");
9905 this.maskEl.bottom.enableDisplayMode("block");
9906 this.maskEl.right.enableDisplayMode("block");
9908 this.toolTip = new Roo.bootstrap.Tooltip({
9909 cls : 'roo-form-error-popover',
9911 'left' : ['r-l', [-2,0], 'right'],
9912 'right' : ['l-r', [2,0], 'left'],
9913 'bottom' : ['tl-bl', [0,2], 'top'],
9914 'top' : [ 'bl-tl', [0,-2], 'bottom']
9918 this.toolTip.render(Roo.get(document.body));
9920 this.toolTip.el.enableDisplayMode("block");
9922 Roo.get(document.body).on('click', function(){
9926 Roo.get(document.body).on('touchstart', function(){
9930 this.isApplied = true
9933 mask : function(form, target)
9937 this.target = target;
9939 if(!this.form.errorMask || !target.el){
9943 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9945 Roo.log(scrollable);
9947 var ot = this.target.el.calcOffsetsTo(scrollable);
9949 var scrollTo = ot[1] - this.form.maskOffset;
9951 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9953 scrollable.scrollTo('top', scrollTo);
9955 var box = this.target.el.getBox();
9957 var zIndex = Roo.bootstrap.Modal.zIndex++;
9960 this.maskEl.top.setStyle('position', 'absolute');
9961 this.maskEl.top.setStyle('z-index', zIndex);
9962 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9963 this.maskEl.top.setLeft(0);
9964 this.maskEl.top.setTop(0);
9965 this.maskEl.top.show();
9967 this.maskEl.left.setStyle('position', 'absolute');
9968 this.maskEl.left.setStyle('z-index', zIndex);
9969 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9970 this.maskEl.left.setLeft(0);
9971 this.maskEl.left.setTop(box.y - this.padding);
9972 this.maskEl.left.show();
9974 this.maskEl.bottom.setStyle('position', 'absolute');
9975 this.maskEl.bottom.setStyle('z-index', zIndex);
9976 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9977 this.maskEl.bottom.setLeft(0);
9978 this.maskEl.bottom.setTop(box.bottom + this.padding);
9979 this.maskEl.bottom.show();
9981 this.maskEl.right.setStyle('position', 'absolute');
9982 this.maskEl.right.setStyle('z-index', zIndex);
9983 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9984 this.maskEl.right.setLeft(box.right + this.padding);
9985 this.maskEl.right.setTop(box.y - this.padding);
9986 this.maskEl.right.show();
9988 this.toolTip.bindEl = this.target.el;
9990 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9992 var tip = this.target.blankText;
9994 if(this.target.getValue() !== '' ) {
9996 if (this.target.invalidText.length) {
9997 tip = this.target.invalidText;
9998 } else if (this.target.regexText.length){
9999 tip = this.target.regexText;
10003 this.toolTip.show(tip);
10005 this.intervalID = window.setInterval(function() {
10006 Roo.bootstrap.Form.popover.unmask();
10009 window.onwheel = function(){ return false;};
10011 (function(){ this.isMasked = true; }).defer(500, this);
10015 unmask : function()
10017 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10021 this.maskEl.top.setStyle('position', 'absolute');
10022 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10023 this.maskEl.top.hide();
10025 this.maskEl.left.setStyle('position', 'absolute');
10026 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10027 this.maskEl.left.hide();
10029 this.maskEl.bottom.setStyle('position', 'absolute');
10030 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10031 this.maskEl.bottom.hide();
10033 this.maskEl.right.setStyle('position', 'absolute');
10034 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10035 this.maskEl.right.hide();
10037 this.toolTip.hide();
10039 this.toolTip.el.hide();
10041 window.onwheel = function(){ return true;};
10043 if(this.intervalID){
10044 window.clearInterval(this.intervalID);
10045 this.intervalID = false;
10048 this.isMasked = false;
10058 * Ext JS Library 1.1.1
10059 * Copyright(c) 2006-2007, Ext JS, LLC.
10061 * Originally Released Under LGPL - original licence link has changed is not relivant.
10064 * <script type="text/javascript">
10067 * @class Roo.form.VTypes
10068 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10071 Roo.form.VTypes = function(){
10072 // closure these in so they are only created once.
10073 var alpha = /^[a-zA-Z_]+$/;
10074 var alphanum = /^[a-zA-Z0-9_]+$/;
10075 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10076 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10078 // All these messages and functions are configurable
10081 * The function used to validate email addresses
10082 * @param {String} value The email address
10084 'email' : function(v){
10085 return email.test(v);
10088 * The error text to display when the email validation function returns false
10091 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10093 * The keystroke filter mask to be applied on email input
10096 'emailMask' : /[a-z0-9_\.\-@]/i,
10099 * The function used to validate URLs
10100 * @param {String} value The URL
10102 'url' : function(v){
10103 return url.test(v);
10106 * The error text to display when the url validation function returns false
10109 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10112 * The function used to validate alpha values
10113 * @param {String} value The value
10115 'alpha' : function(v){
10116 return alpha.test(v);
10119 * The error text to display when the alpha validation function returns false
10122 'alphaText' : 'This field should only contain letters and _',
10124 * The keystroke filter mask to be applied on alpha input
10127 'alphaMask' : /[a-z_]/i,
10130 * The function used to validate alphanumeric values
10131 * @param {String} value The value
10133 'alphanum' : function(v){
10134 return alphanum.test(v);
10137 * The error text to display when the alphanumeric validation function returns false
10140 'alphanumText' : 'This field should only contain letters, numbers and _',
10142 * The keystroke filter mask to be applied on alphanumeric input
10145 'alphanumMask' : /[a-z0-9_]/i
10155 * @class Roo.bootstrap.Input
10156 * @extends Roo.bootstrap.Component
10157 * Bootstrap Input class
10158 * @cfg {Boolean} disabled is it disabled
10159 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10160 * @cfg {String} name name of the input
10161 * @cfg {string} fieldLabel - the label associated
10162 * @cfg {string} placeholder - placeholder to put in text.
10163 * @cfg {string} before - input group add on before
10164 * @cfg {string} after - input group add on after
10165 * @cfg {string} size - (lg|sm) or leave empty..
10166 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10167 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10168 * @cfg {Number} md colspan out of 12 for computer-sized screens
10169 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10170 * @cfg {string} value default value of the input
10171 * @cfg {Number} labelWidth set the width of label
10172 * @cfg {Number} labellg set the width of label (1-12)
10173 * @cfg {Number} labelmd set the width of label (1-12)
10174 * @cfg {Number} labelsm set the width of label (1-12)
10175 * @cfg {Number} labelxs set the width of label (1-12)
10176 * @cfg {String} labelAlign (top|left)
10177 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10178 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10179 * @cfg {String} indicatorpos (left|right) default left
10180 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10181 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10182 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10184 * @cfg {String} align (left|center|right) Default left
10185 * @cfg {Boolean} forceFeedback (true|false) Default false
10188 * Create a new Input
10189 * @param {Object} config The config object
10192 Roo.bootstrap.Input = function(config){
10194 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10199 * Fires when this field receives input focus.
10200 * @param {Roo.form.Field} this
10205 * Fires when this field loses input focus.
10206 * @param {Roo.form.Field} this
10210 * @event specialkey
10211 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10212 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10213 * @param {Roo.form.Field} this
10214 * @param {Roo.EventObject} e The event object
10219 * Fires just before the field blurs if the field value has changed.
10220 * @param {Roo.form.Field} this
10221 * @param {Mixed} newValue The new value
10222 * @param {Mixed} oldValue The original value
10227 * Fires after the field has been marked as invalid.
10228 * @param {Roo.form.Field} this
10229 * @param {String} msg The validation message
10234 * Fires after the field has been validated with no errors.
10235 * @param {Roo.form.Field} this
10240 * Fires after the key up
10241 * @param {Roo.form.Field} this
10242 * @param {Roo.EventObject} e The event Object
10248 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10250 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10251 automatic validation (defaults to "keyup").
10253 validationEvent : "keyup",
10255 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10257 validateOnBlur : true,
10259 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10261 validationDelay : 250,
10263 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10265 focusClass : "x-form-focus", // not needed???
10269 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10271 invalidClass : "has-warning",
10274 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10276 validClass : "has-success",
10279 * @cfg {Boolean} hasFeedback (true|false) default true
10281 hasFeedback : true,
10284 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10286 invalidFeedbackClass : "glyphicon-warning-sign",
10289 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10291 validFeedbackClass : "glyphicon-ok",
10294 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10296 selectOnFocus : false,
10299 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10303 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10308 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10310 disableKeyFilter : false,
10313 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10317 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10321 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10323 blankText : "Please complete this mandatory field",
10326 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10330 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10332 maxLength : Number.MAX_VALUE,
10334 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10336 minLengthText : "The minimum length for this field is {0}",
10338 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10340 maxLengthText : "The maximum length for this field is {0}",
10344 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10345 * If available, this function will be called only after the basic validators all return true, and will be passed the
10346 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10350 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10351 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10352 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10356 * @cfg {String} regexText -- Depricated - use Invalid Text
10361 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10367 autocomplete: false,
10371 inputType : 'text',
10374 placeholder: false,
10379 preventMark: false,
10380 isFormField : true,
10383 labelAlign : false,
10386 formatedValue : false,
10387 forceFeedback : false,
10389 indicatorpos : 'left',
10399 parentLabelAlign : function()
10402 while (parent.parent()) {
10403 parent = parent.parent();
10404 if (typeof(parent.labelAlign) !='undefined') {
10405 return parent.labelAlign;
10412 getAutoCreate : function()
10414 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10420 if(this.inputType != 'hidden'){
10421 cfg.cls = 'form-group' //input-group
10427 type : this.inputType,
10428 value : this.value,
10429 cls : 'form-control',
10430 placeholder : this.placeholder || '',
10431 autocomplete : this.autocomplete || 'new-password'
10434 if(this.capture.length){
10435 input.capture = this.capture;
10438 if(this.accept.length){
10439 input.accept = this.accept + "/*";
10443 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10446 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10447 input.maxLength = this.maxLength;
10450 if (this.disabled) {
10451 input.disabled=true;
10454 if (this.readOnly) {
10455 input.readonly=true;
10459 input.name = this.name;
10463 input.cls += ' input-' + this.size;
10467 ['xs','sm','md','lg'].map(function(size){
10468 if (settings[size]) {
10469 cfg.cls += ' col-' + size + '-' + settings[size];
10473 var inputblock = input;
10477 cls: 'glyphicon form-control-feedback'
10480 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10483 cls : 'has-feedback',
10491 if (this.before || this.after) {
10494 cls : 'input-group',
10498 if (this.before && typeof(this.before) == 'string') {
10500 inputblock.cn.push({
10502 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10506 if (this.before && typeof(this.before) == 'object') {
10507 this.before = Roo.factory(this.before);
10509 inputblock.cn.push({
10511 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10512 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10516 inputblock.cn.push(input);
10518 if (this.after && typeof(this.after) == 'string') {
10519 inputblock.cn.push({
10521 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10525 if (this.after && typeof(this.after) == 'object') {
10526 this.after = Roo.factory(this.after);
10528 inputblock.cn.push({
10530 cls : 'roo-input-after input-group-append input-group-text input-group-' +
10531 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10535 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10536 inputblock.cls += ' has-feedback';
10537 inputblock.cn.push(feedback);
10542 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10543 tooltip : 'This field is required'
10545 if (Roo.bootstrap.version == 4) {
10548 style : 'display-none'
10551 if (align ==='left' && this.fieldLabel.length) {
10553 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10560 cls : 'control-label col-form-label',
10561 html : this.fieldLabel
10572 var labelCfg = cfg.cn[1];
10573 var contentCfg = cfg.cn[2];
10575 if(this.indicatorpos == 'right'){
10580 cls : 'control-label col-form-label',
10584 html : this.fieldLabel
10598 labelCfg = cfg.cn[0];
10599 contentCfg = cfg.cn[1];
10603 if(this.labelWidth > 12){
10604 labelCfg.style = "width: " + this.labelWidth + 'px';
10607 if(this.labelWidth < 13 && this.labelmd == 0){
10608 this.labelmd = this.labelWidth;
10611 if(this.labellg > 0){
10612 labelCfg.cls += ' col-lg-' + this.labellg;
10613 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10616 if(this.labelmd > 0){
10617 labelCfg.cls += ' col-md-' + this.labelmd;
10618 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10621 if(this.labelsm > 0){
10622 labelCfg.cls += ' col-sm-' + this.labelsm;
10623 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10626 if(this.labelxs > 0){
10627 labelCfg.cls += ' col-xs-' + this.labelxs;
10628 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10632 } else if ( this.fieldLabel.length) {
10637 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10638 tooltip : 'This field is required'
10642 //cls : 'input-group-addon',
10643 html : this.fieldLabel
10651 if(this.indicatorpos == 'right'){
10656 //cls : 'input-group-addon',
10657 html : this.fieldLabel
10662 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10663 tooltip : 'This field is required'
10683 if (this.parentType === 'Navbar' && this.parent().bar) {
10684 cfg.cls += ' navbar-form';
10687 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10688 // on BS4 we do this only if not form
10689 cfg.cls += ' navbar-form';
10697 * return the real input element.
10699 inputEl: function ()
10701 return this.el.select('input.form-control',true).first();
10704 tooltipEl : function()
10706 return this.inputEl();
10709 indicatorEl : function()
10711 if (Roo.bootstrap.version == 4) {
10712 return false; // not enabled in v4 yet.
10715 var indicator = this.el.select('i.roo-required-indicator',true).first();
10725 setDisabled : function(v)
10727 var i = this.inputEl().dom;
10729 i.removeAttribute('disabled');
10733 i.setAttribute('disabled','true');
10735 initEvents : function()
10738 this.inputEl().on("keydown" , this.fireKey, this);
10739 this.inputEl().on("focus", this.onFocus, this);
10740 this.inputEl().on("blur", this.onBlur, this);
10742 this.inputEl().relayEvent('keyup', this);
10744 this.indicator = this.indicatorEl();
10746 if(this.indicator){
10747 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10750 // reference to original value for reset
10751 this.originalValue = this.getValue();
10752 //Roo.form.TextField.superclass.initEvents.call(this);
10753 if(this.validationEvent == 'keyup'){
10754 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10755 this.inputEl().on('keyup', this.filterValidation, this);
10757 else if(this.validationEvent !== false){
10758 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10761 if(this.selectOnFocus){
10762 this.on("focus", this.preFocus, this);
10765 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10766 this.inputEl().on("keypress", this.filterKeys, this);
10768 this.inputEl().relayEvent('keypress', this);
10771 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10772 this.el.on("click", this.autoSize, this);
10775 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10776 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10779 if (typeof(this.before) == 'object') {
10780 this.before.render(this.el.select('.roo-input-before',true).first());
10782 if (typeof(this.after) == 'object') {
10783 this.after.render(this.el.select('.roo-input-after',true).first());
10786 this.inputEl().on('change', this.onChange, this);
10789 filterValidation : function(e){
10790 if(!e.isNavKeyPress()){
10791 this.validationTask.delay(this.validationDelay);
10795 * Validates the field value
10796 * @return {Boolean} True if the value is valid, else false
10798 validate : function(){
10799 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10800 if(this.disabled || this.validateValue(this.getRawValue())){
10805 this.markInvalid();
10811 * Validates a value according to the field's validation rules and marks the field as invalid
10812 * if the validation fails
10813 * @param {Mixed} value The value to validate
10814 * @return {Boolean} True if the value is valid, else false
10816 validateValue : function(value)
10818 if(this.getVisibilityEl().hasClass('hidden')){
10822 if(value.length < 1) { // if it's blank
10823 if(this.allowBlank){
10829 if(value.length < this.minLength){
10832 if(value.length > this.maxLength){
10836 var vt = Roo.form.VTypes;
10837 if(!vt[this.vtype](value, this)){
10841 if(typeof this.validator == "function"){
10842 var msg = this.validator(value);
10846 if (typeof(msg) == 'string') {
10847 this.invalidText = msg;
10851 if(this.regex && !this.regex.test(value)){
10859 fireKey : function(e){
10860 //Roo.log('field ' + e.getKey());
10861 if(e.isNavKeyPress()){
10862 this.fireEvent("specialkey", this, e);
10865 focus : function (selectText){
10867 this.inputEl().focus();
10868 if(selectText === true){
10869 this.inputEl().dom.select();
10875 onFocus : function(){
10876 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10877 // this.el.addClass(this.focusClass);
10879 if(!this.hasFocus){
10880 this.hasFocus = true;
10881 this.startValue = this.getValue();
10882 this.fireEvent("focus", this);
10886 beforeBlur : Roo.emptyFn,
10890 onBlur : function(){
10892 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10893 //this.el.removeClass(this.focusClass);
10895 this.hasFocus = false;
10896 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10899 var v = this.getValue();
10900 if(String(v) !== String(this.startValue)){
10901 this.fireEvent('change', this, v, this.startValue);
10903 this.fireEvent("blur", this);
10906 onChange : function(e)
10908 var v = this.getValue();
10909 if(String(v) !== String(this.startValue)){
10910 this.fireEvent('change', this, v, this.startValue);
10916 * Resets the current field value to the originally loaded value and clears any validation messages
10918 reset : function(){
10919 this.setValue(this.originalValue);
10923 * Returns the name of the field
10924 * @return {Mixed} name The name field
10926 getName: function(){
10930 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10931 * @return {Mixed} value The field value
10933 getValue : function(){
10935 var v = this.inputEl().getValue();
10940 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10941 * @return {Mixed} value The field value
10943 getRawValue : function(){
10944 var v = this.inputEl().getValue();
10950 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10951 * @param {Mixed} value The value to set
10953 setRawValue : function(v){
10954 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10957 selectText : function(start, end){
10958 var v = this.getRawValue();
10960 start = start === undefined ? 0 : start;
10961 end = end === undefined ? v.length : end;
10962 var d = this.inputEl().dom;
10963 if(d.setSelectionRange){
10964 d.setSelectionRange(start, end);
10965 }else if(d.createTextRange){
10966 var range = d.createTextRange();
10967 range.moveStart("character", start);
10968 range.moveEnd("character", v.length-end);
10975 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10976 * @param {Mixed} value The value to set
10978 setValue : function(v){
10981 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10987 processValue : function(value){
10988 if(this.stripCharsRe){
10989 var newValue = value.replace(this.stripCharsRe, '');
10990 if(newValue !== value){
10991 this.setRawValue(newValue);
10998 preFocus : function(){
11000 if(this.selectOnFocus){
11001 this.inputEl().dom.select();
11004 filterKeys : function(e){
11005 var k = e.getKey();
11006 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11009 var c = e.getCharCode(), cc = String.fromCharCode(c);
11010 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11013 if(!this.maskRe.test(cc)){
11018 * Clear any invalid styles/messages for this field
11020 clearInvalid : function(){
11022 if(!this.el || this.preventMark){ // not rendered
11027 this.el.removeClass([this.invalidClass, 'is-invalid']);
11029 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11031 var feedback = this.el.select('.form-control-feedback', true).first();
11034 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11039 if(this.indicator){
11040 this.indicator.removeClass('visible');
11041 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11044 this.fireEvent('valid', this);
11048 * Mark this field as valid
11050 markValid : function()
11052 if(!this.el || this.preventMark){ // not rendered...
11056 this.el.removeClass([this.invalidClass, this.validClass]);
11057 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11059 var feedback = this.el.select('.form-control-feedback', true).first();
11062 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11065 if(this.indicator){
11066 this.indicator.removeClass('visible');
11067 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11075 if(this.allowBlank && !this.getRawValue().length){
11078 if (Roo.bootstrap.version == 3) {
11079 this.el.addClass(this.validClass);
11081 this.inputEl().addClass('is-valid');
11084 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11086 var feedback = this.el.select('.form-control-feedback', true).first();
11089 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11090 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11095 this.fireEvent('valid', this);
11099 * Mark this field as invalid
11100 * @param {String} msg The validation message
11102 markInvalid : function(msg)
11104 if(!this.el || this.preventMark){ // not rendered
11108 this.el.removeClass([this.invalidClass, this.validClass]);
11109 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11111 var feedback = this.el.select('.form-control-feedback', true).first();
11114 this.el.select('.form-control-feedback', true).first().removeClass(
11115 [this.invalidFeedbackClass, this.validFeedbackClass]);
11122 if(this.allowBlank && !this.getRawValue().length){
11126 if(this.indicator){
11127 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11128 this.indicator.addClass('visible');
11130 if (Roo.bootstrap.version == 3) {
11131 this.el.addClass(this.invalidClass);
11133 this.inputEl().addClass('is-invalid');
11138 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11140 var feedback = this.el.select('.form-control-feedback', true).first();
11143 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11145 if(this.getValue().length || this.forceFeedback){
11146 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11153 this.fireEvent('invalid', this, msg);
11156 SafariOnKeyDown : function(event)
11158 // this is a workaround for a password hang bug on chrome/ webkit.
11159 if (this.inputEl().dom.type != 'password') {
11163 var isSelectAll = false;
11165 if(this.inputEl().dom.selectionEnd > 0){
11166 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11168 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11169 event.preventDefault();
11174 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11176 event.preventDefault();
11177 // this is very hacky as keydown always get's upper case.
11179 var cc = String.fromCharCode(event.getCharCode());
11180 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11184 adjustWidth : function(tag, w){
11185 tag = tag.toLowerCase();
11186 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11187 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11188 if(tag == 'input'){
11191 if(tag == 'textarea'){
11194 }else if(Roo.isOpera){
11195 if(tag == 'input'){
11198 if(tag == 'textarea'){
11206 setFieldLabel : function(v)
11208 if(!this.rendered){
11212 if(this.indicatorEl()){
11213 var ar = this.el.select('label > span',true);
11215 if (ar.elements.length) {
11216 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11217 this.fieldLabel = v;
11221 var br = this.el.select('label',true);
11223 if(br.elements.length) {
11224 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11225 this.fieldLabel = v;
11229 Roo.log('Cannot Found any of label > span || label in input');
11233 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11234 this.fieldLabel = v;
11249 * @class Roo.bootstrap.TextArea
11250 * @extends Roo.bootstrap.Input
11251 * Bootstrap TextArea class
11252 * @cfg {Number} cols Specifies the visible width of a text area
11253 * @cfg {Number} rows Specifies the visible number of lines in a text area
11254 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11255 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11256 * @cfg {string} html text
11259 * Create a new TextArea
11260 * @param {Object} config The config object
11263 Roo.bootstrap.TextArea = function(config){
11264 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11268 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11278 getAutoCreate : function(){
11280 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11286 if(this.inputType != 'hidden'){
11287 cfg.cls = 'form-group' //input-group
11295 value : this.value || '',
11296 html: this.html || '',
11297 cls : 'form-control',
11298 placeholder : this.placeholder || ''
11302 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11303 input.maxLength = this.maxLength;
11307 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11311 input.cols = this.cols;
11314 if (this.readOnly) {
11315 input.readonly = true;
11319 input.name = this.name;
11323 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11327 ['xs','sm','md','lg'].map(function(size){
11328 if (settings[size]) {
11329 cfg.cls += ' col-' + size + '-' + settings[size];
11333 var inputblock = input;
11335 if(this.hasFeedback && !this.allowBlank){
11339 cls: 'glyphicon form-control-feedback'
11343 cls : 'has-feedback',
11352 if (this.before || this.after) {
11355 cls : 'input-group',
11359 inputblock.cn.push({
11361 cls : 'input-group-addon',
11366 inputblock.cn.push(input);
11368 if(this.hasFeedback && !this.allowBlank){
11369 inputblock.cls += ' has-feedback';
11370 inputblock.cn.push(feedback);
11374 inputblock.cn.push({
11376 cls : 'input-group-addon',
11383 if (align ==='left' && this.fieldLabel.length) {
11388 cls : 'control-label',
11389 html : this.fieldLabel
11400 if(this.labelWidth > 12){
11401 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11404 if(this.labelWidth < 13 && this.labelmd == 0){
11405 this.labelmd = this.labelWidth;
11408 if(this.labellg > 0){
11409 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11410 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11413 if(this.labelmd > 0){
11414 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11415 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11418 if(this.labelsm > 0){
11419 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11420 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11423 if(this.labelxs > 0){
11424 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11425 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11428 } else if ( this.fieldLabel.length) {
11433 //cls : 'input-group-addon',
11434 html : this.fieldLabel
11452 if (this.disabled) {
11453 input.disabled=true;
11460 * return the real textarea element.
11462 inputEl: function ()
11464 return this.el.select('textarea.form-control',true).first();
11468 * Clear any invalid styles/messages for this field
11470 clearInvalid : function()
11473 if(!this.el || this.preventMark){ // not rendered
11477 var label = this.el.select('label', true).first();
11478 var icon = this.el.select('i.fa-star', true).first();
11483 this.el.removeClass( this.validClass);
11484 this.inputEl().removeClass('is-invalid');
11486 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11488 var feedback = this.el.select('.form-control-feedback', true).first();
11491 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11496 this.fireEvent('valid', this);
11500 * Mark this field as valid
11502 markValid : function()
11504 if(!this.el || this.preventMark){ // not rendered
11508 this.el.removeClass([this.invalidClass, this.validClass]);
11509 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11511 var feedback = this.el.select('.form-control-feedback', true).first();
11514 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11517 if(this.disabled || this.allowBlank){
11521 var label = this.el.select('label', true).first();
11522 var icon = this.el.select('i.fa-star', true).first();
11527 if (Roo.bootstrap.version == 3) {
11528 this.el.addClass(this.validClass);
11530 this.inputEl().addClass('is-valid');
11534 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11536 var feedback = this.el.select('.form-control-feedback', true).first();
11539 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11540 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11545 this.fireEvent('valid', this);
11549 * Mark this field as invalid
11550 * @param {String} msg The validation message
11552 markInvalid : function(msg)
11554 if(!this.el || this.preventMark){ // not rendered
11558 this.el.removeClass([this.invalidClass, this.validClass]);
11559 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11561 var feedback = this.el.select('.form-control-feedback', true).first();
11564 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11567 if(this.disabled || this.allowBlank){
11571 var label = this.el.select('label', true).first();
11572 var icon = this.el.select('i.fa-star', true).first();
11574 if(!this.getValue().length && label && !icon){
11575 this.el.createChild({
11577 cls : 'text-danger fa fa-lg fa-star',
11578 tooltip : 'This field is required',
11579 style : 'margin-right:5px;'
11583 if (Roo.bootstrap.version == 3) {
11584 this.el.addClass(this.invalidClass);
11586 this.inputEl().addClass('is-invalid');
11589 // fixme ... this may be depricated need to test..
11590 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11592 var feedback = this.el.select('.form-control-feedback', true).first();
11595 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11597 if(this.getValue().length || this.forceFeedback){
11598 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11605 this.fireEvent('invalid', this, msg);
11613 * trigger field - base class for combo..
11618 * @class Roo.bootstrap.TriggerField
11619 * @extends Roo.bootstrap.Input
11620 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11621 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11622 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11623 * for which you can provide a custom implementation. For example:
11625 var trigger = new Roo.bootstrap.TriggerField();
11626 trigger.onTriggerClick = myTriggerFn;
11627 trigger.applyTo('my-field');
11630 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11631 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11632 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11633 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11634 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11637 * Create a new TriggerField.
11638 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11639 * to the base TextField)
11641 Roo.bootstrap.TriggerField = function(config){
11642 this.mimicing = false;
11643 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11646 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11648 * @cfg {String} triggerClass A CSS class to apply to the trigger
11651 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11656 * @cfg {Boolean} removable (true|false) special filter default false
11660 /** @cfg {Boolean} grow @hide */
11661 /** @cfg {Number} growMin @hide */
11662 /** @cfg {Number} growMax @hide */
11668 autoSize: Roo.emptyFn,
11672 deferHeight : true,
11675 actionMode : 'wrap',
11680 getAutoCreate : function(){
11682 var align = this.labelAlign || this.parentLabelAlign();
11687 cls: 'form-group' //input-group
11694 type : this.inputType,
11695 cls : 'form-control',
11696 autocomplete: 'new-password',
11697 placeholder : this.placeholder || ''
11701 input.name = this.name;
11704 input.cls += ' input-' + this.size;
11707 if (this.disabled) {
11708 input.disabled=true;
11711 var inputblock = input;
11713 if(this.hasFeedback && !this.allowBlank){
11717 cls: 'glyphicon form-control-feedback'
11720 if(this.removable && !this.editable ){
11722 cls : 'has-feedback',
11728 cls : 'roo-combo-removable-btn close'
11735 cls : 'has-feedback',
11744 if(this.removable && !this.editable ){
11746 cls : 'roo-removable',
11752 cls : 'roo-combo-removable-btn close'
11759 if (this.before || this.after) {
11762 cls : 'input-group',
11766 inputblock.cn.push({
11768 cls : 'input-group-addon input-group-prepend input-group-text',
11773 inputblock.cn.push(input);
11775 if(this.hasFeedback && !this.allowBlank){
11776 inputblock.cls += ' has-feedback';
11777 inputblock.cn.push(feedback);
11781 inputblock.cn.push({
11783 cls : 'input-group-addon input-group-append input-group-text',
11792 var ibwrap = inputblock;
11797 cls: 'roo-select2-choices',
11801 cls: 'roo-select2-search-field',
11813 cls: 'roo-select2-container input-group',
11818 cls: 'form-hidden-field'
11824 if(!this.multiple && this.showToggleBtn){
11830 if (this.caret != false) {
11833 cls: 'fa fa-' + this.caret
11840 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11842 Roo.bootstrap.version == 3 ? caret : '',
11845 cls: 'combobox-clear',
11859 combobox.cls += ' roo-select2-container-multi';
11863 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11864 tooltip : 'This field is required'
11866 if (Roo.bootstrap.version == 4) {
11869 style : 'display:none'
11874 if (align ==='left' && this.fieldLabel.length) {
11876 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11883 cls : 'control-label',
11884 html : this.fieldLabel
11896 var labelCfg = cfg.cn[1];
11897 var contentCfg = cfg.cn[2];
11899 if(this.indicatorpos == 'right'){
11904 cls : 'control-label',
11908 html : this.fieldLabel
11922 labelCfg = cfg.cn[0];
11923 contentCfg = cfg.cn[1];
11926 if(this.labelWidth > 12){
11927 labelCfg.style = "width: " + this.labelWidth + 'px';
11930 if(this.labelWidth < 13 && this.labelmd == 0){
11931 this.labelmd = this.labelWidth;
11934 if(this.labellg > 0){
11935 labelCfg.cls += ' col-lg-' + this.labellg;
11936 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11939 if(this.labelmd > 0){
11940 labelCfg.cls += ' col-md-' + this.labelmd;
11941 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11944 if(this.labelsm > 0){
11945 labelCfg.cls += ' col-sm-' + this.labelsm;
11946 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11949 if(this.labelxs > 0){
11950 labelCfg.cls += ' col-xs-' + this.labelxs;
11951 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11954 } else if ( this.fieldLabel.length) {
11955 // Roo.log(" label");
11960 //cls : 'input-group-addon',
11961 html : this.fieldLabel
11969 if(this.indicatorpos == 'right'){
11977 html : this.fieldLabel
11991 // Roo.log(" no label && no align");
11998 ['xs','sm','md','lg'].map(function(size){
11999 if (settings[size]) {
12000 cfg.cls += ' col-' + size + '-' + settings[size];
12011 onResize : function(w, h){
12012 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12013 // if(typeof w == 'number'){
12014 // var x = w - this.trigger.getWidth();
12015 // this.inputEl().setWidth(this.adjustWidth('input', x));
12016 // this.trigger.setStyle('left', x+'px');
12021 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12024 getResizeEl : function(){
12025 return this.inputEl();
12029 getPositionEl : function(){
12030 return this.inputEl();
12034 alignErrorIcon : function(){
12035 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12039 initEvents : function(){
12043 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12044 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12045 if(!this.multiple && this.showToggleBtn){
12046 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12047 if(this.hideTrigger){
12048 this.trigger.setDisplayed(false);
12050 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12054 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12057 if(this.removable && !this.editable && !this.tickable){
12058 var close = this.closeTriggerEl();
12061 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12062 close.on('click', this.removeBtnClick, this, close);
12066 //this.trigger.addClassOnOver('x-form-trigger-over');
12067 //this.trigger.addClassOnClick('x-form-trigger-click');
12070 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12074 closeTriggerEl : function()
12076 var close = this.el.select('.roo-combo-removable-btn', true).first();
12077 return close ? close : false;
12080 removeBtnClick : function(e, h, el)
12082 e.preventDefault();
12084 if(this.fireEvent("remove", this) !== false){
12086 this.fireEvent("afterremove", this)
12090 createList : function()
12092 this.list = Roo.get(document.body).createChild({
12093 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12094 cls: 'typeahead typeahead-long dropdown-menu',
12095 style: 'display:none'
12098 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12103 initTrigger : function(){
12108 onDestroy : function(){
12110 this.trigger.removeAllListeners();
12111 // this.trigger.remove();
12114 // this.wrap.remove();
12116 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12120 onFocus : function(){
12121 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12123 if(!this.mimicing){
12124 this.wrap.addClass('x-trigger-wrap-focus');
12125 this.mimicing = true;
12126 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12127 if(this.monitorTab){
12128 this.el.on("keydown", this.checkTab, this);
12135 checkTab : function(e){
12136 if(e.getKey() == e.TAB){
12137 this.triggerBlur();
12142 onBlur : function(){
12147 mimicBlur : function(e, t){
12149 if(!this.wrap.contains(t) && this.validateBlur()){
12150 this.triggerBlur();
12156 triggerBlur : function(){
12157 this.mimicing = false;
12158 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12159 if(this.monitorTab){
12160 this.el.un("keydown", this.checkTab, this);
12162 //this.wrap.removeClass('x-trigger-wrap-focus');
12163 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12167 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12168 validateBlur : function(e, t){
12173 onDisable : function(){
12174 this.inputEl().dom.disabled = true;
12175 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12177 // this.wrap.addClass('x-item-disabled');
12182 onEnable : function(){
12183 this.inputEl().dom.disabled = false;
12184 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12186 // this.el.removeClass('x-item-disabled');
12191 onShow : function(){
12192 var ae = this.getActionEl();
12195 ae.dom.style.display = '';
12196 ae.dom.style.visibility = 'visible';
12202 onHide : function(){
12203 var ae = this.getActionEl();
12204 ae.dom.style.display = 'none';
12208 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12209 * by an implementing function.
12211 * @param {EventObject} e
12213 onTriggerClick : Roo.emptyFn
12221 * @class Roo.bootstrap.CardUploader
12222 * @extends Roo.bootstrap.Button
12223 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12224 * @cfg {Number} errorTimeout default 3000
12225 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12226 * @cfg {Array} html The button text.
12230 * Create a new CardUploader
12231 * @param {Object} config The config object
12234 Roo.bootstrap.CardUploader = function(config){
12238 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12241 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12248 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12251 errorTimeout : 3000,
12255 fileCollection : false,
12258 getAutoCreate : function()
12262 cls :'form-group' ,
12267 //cls : 'input-group-addon',
12268 html : this.fieldLabel
12275 value : this.value,
12276 cls : 'd-none form-control'
12281 multiple : 'multiple',
12283 cls : 'd-none roo-card-upload-selector'
12287 cls : 'roo-card-uploader-button-container w-100 mb-2'
12290 cls : 'card-columns roo-card-uploader-container'
12300 getChildContainer : function() /// what children are added to.
12302 return this.containerEl;
12305 getButtonContainer : function() /// what children are added to.
12307 return this.el.select(".roo-card-uploader-button-container").first();
12310 initEvents : function()
12313 Roo.bootstrap.Input.prototype.initEvents.call(this);
12317 xns: Roo.bootstrap,
12320 container_method : 'getButtonContainer' ,
12321 html : this.html, // fix changable?
12324 'click' : function(btn, e) {
12333 this.urlAPI = (window.createObjectURL && window) ||
12334 (window.URL && URL.revokeObjectURL && URL) ||
12335 (window.webkitURL && webkitURL);
12340 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12342 this.selectorEl.on('change', this.onFileSelected, this);
12345 this.images.forEach(function(img) {
12348 this.images = false;
12350 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12356 onClick : function(e)
12358 e.preventDefault();
12360 this.selectorEl.dom.click();
12364 onFileSelected : function(e)
12366 e.preventDefault();
12368 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12372 Roo.each(this.selectorEl.dom.files, function(file){
12373 this.addFile(file);
12382 addFile : function(file)
12385 if(typeof(file) === 'string'){
12386 throw "Add file by name?"; // should not happen
12390 if(!file || !this.urlAPI){
12400 var url = _this.urlAPI.createObjectURL( file);
12403 id : Roo.bootstrap.CardUploader.ID--,
12404 is_uploaded : false,
12407 mimetype : file.type,
12414 addCard : function (data)
12416 // hidden input element?
12417 // if the file is not an image...
12418 //then we need to use something other that and header_image
12423 xns : Roo.bootstrap,
12424 xtype : 'CardFooter',
12427 xns : Roo.bootstrap,
12433 xns : Roo.bootstrap,
12435 html : String.format("<small>{0}</small>", data.title),
12436 cls : 'col-11 text-left',
12441 click : function() {
12442 this.downloadCard(data.id)
12448 xns : Roo.bootstrap,
12456 click : function() {
12457 t.removeCard(data.id)
12469 var cn = this.addxtype(
12472 xns : Roo.bootstrap,
12475 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12476 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12477 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12482 initEvents : function() {
12483 Roo.bootstrap.Card.prototype.initEvents.call(this);
12484 this.imgEl = this.el.select('.card-img-top').first();
12486 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12487 this.imgEl.set({ 'pointer' : 'cursor' });
12496 // dont' really need ot update items.
12497 // this.items.push(cn);
12498 this.fileCollection.add(cn);
12499 this.updateInput();
12502 removeCard : function(id)
12505 var card = this.fileCollection.get(id);
12506 card.data.is_deleted = 1;
12507 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12508 this.fileCollection.remove(card);
12509 //this.items = this.items.filter(function(e) { return e != card });
12510 // dont' really need ot update items.
12511 card.el.dom.parentNode.removeChild(card.el.dom);
12516 this.fileCollection.each(function(card) {
12517 card.el.dom.parentNode.removeChild(card.el.dom);
12519 this.fileCollection.clear();
12520 this.updateInput();
12523 updateInput : function()
12526 this.fileCollection.each(function(e) {
12530 this.inputEl().dom.value = JSON.stringify(data);
12537 Roo.bootstrap.CardUploader.ID = -1;/*
12539 * Ext JS Library 1.1.1
12540 * Copyright(c) 2006-2007, Ext JS, LLC.
12542 * Originally Released Under LGPL - original licence link has changed is not relivant.
12545 * <script type="text/javascript">
12550 * @class Roo.data.SortTypes
12552 * Defines the default sorting (casting?) comparison functions used when sorting data.
12554 Roo.data.SortTypes = {
12556 * Default sort that does nothing
12557 * @param {Mixed} s The value being converted
12558 * @return {Mixed} The comparison value
12560 none : function(s){
12565 * The regular expression used to strip tags
12569 stripTagsRE : /<\/?[^>]+>/gi,
12572 * Strips all HTML tags to sort on text only
12573 * @param {Mixed} s The value being converted
12574 * @return {String} The comparison value
12576 asText : function(s){
12577 return String(s).replace(this.stripTagsRE, "");
12581 * Strips all HTML tags to sort on text only - Case insensitive
12582 * @param {Mixed} s The value being converted
12583 * @return {String} The comparison value
12585 asUCText : function(s){
12586 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12590 * Case insensitive string
12591 * @param {Mixed} s The value being converted
12592 * @return {String} The comparison value
12594 asUCString : function(s) {
12595 return String(s).toUpperCase();
12600 * @param {Mixed} s The value being converted
12601 * @return {Number} The comparison value
12603 asDate : function(s) {
12607 if(s instanceof Date){
12608 return s.getTime();
12610 return Date.parse(String(s));
12615 * @param {Mixed} s The value being converted
12616 * @return {Float} The comparison value
12618 asFloat : function(s) {
12619 var val = parseFloat(String(s).replace(/,/g, ""));
12628 * @param {Mixed} s The value being converted
12629 * @return {Number} The comparison value
12631 asInt : function(s) {
12632 var val = parseInt(String(s).replace(/,/g, ""));
12640 * Ext JS Library 1.1.1
12641 * Copyright(c) 2006-2007, Ext JS, LLC.
12643 * Originally Released Under LGPL - original licence link has changed is not relivant.
12646 * <script type="text/javascript">
12650 * @class Roo.data.Record
12651 * Instances of this class encapsulate both record <em>definition</em> information, and record
12652 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12653 * to access Records cached in an {@link Roo.data.Store} object.<br>
12655 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12656 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12659 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12661 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12662 * {@link #create}. The parameters are the same.
12663 * @param {Array} data An associative Array of data values keyed by the field name.
12664 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12665 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12666 * not specified an integer id is generated.
12668 Roo.data.Record = function(data, id){
12669 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12674 * Generate a constructor for a specific record layout.
12675 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12676 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12677 * Each field definition object may contain the following properties: <ul>
12678 * <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,
12679 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12680 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12681 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12682 * is being used, then this is a string containing the javascript expression to reference the data relative to
12683 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12684 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12685 * this may be omitted.</p></li>
12686 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12687 * <ul><li>auto (Default, implies no conversion)</li>
12692 * <li>date</li></ul></p></li>
12693 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12694 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12695 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12696 * by the Reader into an object that will be stored in the Record. It is passed the
12697 * following parameters:<ul>
12698 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12700 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12702 * <br>usage:<br><pre><code>
12703 var TopicRecord = Roo.data.Record.create(
12704 {name: 'title', mapping: 'topic_title'},
12705 {name: 'author', mapping: 'username'},
12706 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12707 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12708 {name: 'lastPoster', mapping: 'user2'},
12709 {name: 'excerpt', mapping: 'post_text'}
12712 var myNewRecord = new TopicRecord({
12713 title: 'Do my job please',
12716 lastPost: new Date(),
12717 lastPoster: 'Animal',
12718 excerpt: 'No way dude!'
12720 myStore.add(myNewRecord);
12725 Roo.data.Record.create = function(o){
12726 var f = function(){
12727 f.superclass.constructor.apply(this, arguments);
12729 Roo.extend(f, Roo.data.Record);
12730 var p = f.prototype;
12731 p.fields = new Roo.util.MixedCollection(false, function(field){
12734 for(var i = 0, len = o.length; i < len; i++){
12735 p.fields.add(new Roo.data.Field(o[i]));
12737 f.getField = function(name){
12738 return p.fields.get(name);
12743 Roo.data.Record.AUTO_ID = 1000;
12744 Roo.data.Record.EDIT = 'edit';
12745 Roo.data.Record.REJECT = 'reject';
12746 Roo.data.Record.COMMIT = 'commit';
12748 Roo.data.Record.prototype = {
12750 * Readonly flag - true if this record has been modified.
12759 join : function(store){
12760 this.store = store;
12764 * Set the named field to the specified value.
12765 * @param {String} name The name of the field to set.
12766 * @param {Object} value The value to set the field to.
12768 set : function(name, value){
12769 if(this.data[name] == value){
12773 if(!this.modified){
12774 this.modified = {};
12776 if(typeof this.modified[name] == 'undefined'){
12777 this.modified[name] = this.data[name];
12779 this.data[name] = value;
12780 if(!this.editing && this.store){
12781 this.store.afterEdit(this);
12786 * Get the value of the named field.
12787 * @param {String} name The name of the field to get the value of.
12788 * @return {Object} The value of the field.
12790 get : function(name){
12791 return this.data[name];
12795 beginEdit : function(){
12796 this.editing = true;
12797 this.modified = {};
12801 cancelEdit : function(){
12802 this.editing = false;
12803 delete this.modified;
12807 endEdit : function(){
12808 this.editing = false;
12809 if(this.dirty && this.store){
12810 this.store.afterEdit(this);
12815 * Usually called by the {@link Roo.data.Store} which owns the Record.
12816 * Rejects all changes made to the Record since either creation, or the last commit operation.
12817 * Modified fields are reverted to their original values.
12819 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12820 * of reject operations.
12822 reject : function(){
12823 var m = this.modified;
12825 if(typeof m[n] != "function"){
12826 this.data[n] = m[n];
12829 this.dirty = false;
12830 delete this.modified;
12831 this.editing = false;
12833 this.store.afterReject(this);
12838 * Usually called by the {@link Roo.data.Store} which owns the Record.
12839 * Commits all changes made to the Record since either creation, or the last commit operation.
12841 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12842 * of commit operations.
12844 commit : function(){
12845 this.dirty = false;
12846 delete this.modified;
12847 this.editing = false;
12849 this.store.afterCommit(this);
12854 hasError : function(){
12855 return this.error != null;
12859 clearError : function(){
12864 * Creates a copy of this record.
12865 * @param {String} id (optional) A new record id if you don't want to use this record's id
12868 copy : function(newId) {
12869 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12873 * Ext JS Library 1.1.1
12874 * Copyright(c) 2006-2007, Ext JS, LLC.
12876 * Originally Released Under LGPL - original licence link has changed is not relivant.
12879 * <script type="text/javascript">
12885 * @class Roo.data.Store
12886 * @extends Roo.util.Observable
12887 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12888 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12890 * 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
12891 * has no knowledge of the format of the data returned by the Proxy.<br>
12893 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12894 * instances from the data object. These records are cached and made available through accessor functions.
12896 * Creates a new Store.
12897 * @param {Object} config A config object containing the objects needed for the Store to access data,
12898 * and read the data into Records.
12900 Roo.data.Store = function(config){
12901 this.data = new Roo.util.MixedCollection(false);
12902 this.data.getKey = function(o){
12905 this.baseParams = {};
12907 this.paramNames = {
12912 "multisort" : "_multisort"
12915 if(config && config.data){
12916 this.inlineData = config.data;
12917 delete config.data;
12920 Roo.apply(this, config);
12922 if(this.reader){ // reader passed
12923 this.reader = Roo.factory(this.reader, Roo.data);
12924 this.reader.xmodule = this.xmodule || false;
12925 if(!this.recordType){
12926 this.recordType = this.reader.recordType;
12928 if(this.reader.onMetaChange){
12929 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12933 if(this.recordType){
12934 this.fields = this.recordType.prototype.fields;
12936 this.modified = [];
12940 * @event datachanged
12941 * Fires when the data cache has changed, and a widget which is using this Store
12942 * as a Record cache should refresh its view.
12943 * @param {Store} this
12945 datachanged : true,
12947 * @event metachange
12948 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12949 * @param {Store} this
12950 * @param {Object} meta The JSON metadata
12955 * Fires when Records have been added to the Store
12956 * @param {Store} this
12957 * @param {Roo.data.Record[]} records The array of Records added
12958 * @param {Number} index The index at which the record(s) were added
12963 * Fires when a Record has been removed from the Store
12964 * @param {Store} this
12965 * @param {Roo.data.Record} record The Record that was removed
12966 * @param {Number} index The index at which the record was removed
12971 * Fires when a Record has been updated
12972 * @param {Store} this
12973 * @param {Roo.data.Record} record The Record that was updated
12974 * @param {String} operation The update operation being performed. Value may be one of:
12976 Roo.data.Record.EDIT
12977 Roo.data.Record.REJECT
12978 Roo.data.Record.COMMIT
12984 * Fires when the data cache has been cleared.
12985 * @param {Store} this
12989 * @event beforeload
12990 * Fires before a request is made for a new data object. If the beforeload handler returns false
12991 * the load action will be canceled.
12992 * @param {Store} this
12993 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12997 * @event beforeloadadd
12998 * Fires after a new set of Records has been loaded.
12999 * @param {Store} this
13000 * @param {Roo.data.Record[]} records The Records that were loaded
13001 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13003 beforeloadadd : true,
13006 * Fires after a new set of Records has been loaded, before they are added to the store.
13007 * @param {Store} this
13008 * @param {Roo.data.Record[]} records The Records that were loaded
13009 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13010 * @params {Object} return from reader
13014 * @event loadexception
13015 * Fires if an exception occurs in the Proxy during loading.
13016 * Called with the signature of the Proxy's "loadexception" event.
13017 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13020 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13021 * @param {Object} load options
13022 * @param {Object} jsonData from your request (normally this contains the Exception)
13024 loadexception : true
13028 this.proxy = Roo.factory(this.proxy, Roo.data);
13029 this.proxy.xmodule = this.xmodule || false;
13030 this.relayEvents(this.proxy, ["loadexception"]);
13032 this.sortToggle = {};
13033 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13035 Roo.data.Store.superclass.constructor.call(this);
13037 if(this.inlineData){
13038 this.loadData(this.inlineData);
13039 delete this.inlineData;
13043 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13045 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13046 * without a remote query - used by combo/forms at present.
13050 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13053 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13056 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13057 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13060 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13061 * on any HTTP request
13064 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13067 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13071 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13072 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13074 remoteSort : false,
13077 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13078 * loaded or when a record is removed. (defaults to false).
13080 pruneModifiedRecords : false,
13083 lastOptions : null,
13086 * Add Records to the Store and fires the add event.
13087 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13089 add : function(records){
13090 records = [].concat(records);
13091 for(var i = 0, len = records.length; i < len; i++){
13092 records[i].join(this);
13094 var index = this.data.length;
13095 this.data.addAll(records);
13096 this.fireEvent("add", this, records, index);
13100 * Remove a Record from the Store and fires the remove event.
13101 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13103 remove : function(record){
13104 var index = this.data.indexOf(record);
13105 this.data.removeAt(index);
13107 if(this.pruneModifiedRecords){
13108 this.modified.remove(record);
13110 this.fireEvent("remove", this, record, index);
13114 * Remove all Records from the Store and fires the clear event.
13116 removeAll : function(){
13118 if(this.pruneModifiedRecords){
13119 this.modified = [];
13121 this.fireEvent("clear", this);
13125 * Inserts Records to the Store at the given index and fires the add event.
13126 * @param {Number} index The start index at which to insert the passed Records.
13127 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13129 insert : function(index, records){
13130 records = [].concat(records);
13131 for(var i = 0, len = records.length; i < len; i++){
13132 this.data.insert(index, records[i]);
13133 records[i].join(this);
13135 this.fireEvent("add", this, records, index);
13139 * Get the index within the cache of the passed Record.
13140 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13141 * @return {Number} The index of the passed Record. Returns -1 if not found.
13143 indexOf : function(record){
13144 return this.data.indexOf(record);
13148 * Get the index within the cache of the Record with the passed id.
13149 * @param {String} id The id of the Record to find.
13150 * @return {Number} The index of the Record. Returns -1 if not found.
13152 indexOfId : function(id){
13153 return this.data.indexOfKey(id);
13157 * Get the Record with the specified id.
13158 * @param {String} id The id of the Record to find.
13159 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13161 getById : function(id){
13162 return this.data.key(id);
13166 * Get the Record at the specified index.
13167 * @param {Number} index The index of the Record to find.
13168 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13170 getAt : function(index){
13171 return this.data.itemAt(index);
13175 * Returns a range of Records between specified indices.
13176 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13177 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13178 * @return {Roo.data.Record[]} An array of Records
13180 getRange : function(start, end){
13181 return this.data.getRange(start, end);
13185 storeOptions : function(o){
13186 o = Roo.apply({}, o);
13189 this.lastOptions = o;
13193 * Loads the Record cache from the configured Proxy using the configured Reader.
13195 * If using remote paging, then the first load call must specify the <em>start</em>
13196 * and <em>limit</em> properties in the options.params property to establish the initial
13197 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13199 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13200 * and this call will return before the new data has been loaded. Perform any post-processing
13201 * in a callback function, or in a "load" event handler.</strong>
13203 * @param {Object} options An object containing properties which control loading options:<ul>
13204 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13205 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13206 * passed the following arguments:<ul>
13207 * <li>r : Roo.data.Record[]</li>
13208 * <li>options: Options object from the load call</li>
13209 * <li>success: Boolean success indicator</li></ul></li>
13210 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13211 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13214 load : function(options){
13215 options = options || {};
13216 if(this.fireEvent("beforeload", this, options) !== false){
13217 this.storeOptions(options);
13218 var p = Roo.apply(options.params || {}, this.baseParams);
13219 // if meta was not loaded from remote source.. try requesting it.
13220 if (!this.reader.metaFromRemote) {
13221 p._requestMeta = 1;
13223 if(this.sortInfo && this.remoteSort){
13224 var pn = this.paramNames;
13225 p[pn["sort"]] = this.sortInfo.field;
13226 p[pn["dir"]] = this.sortInfo.direction;
13228 if (this.multiSort) {
13229 var pn = this.paramNames;
13230 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13233 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13238 * Reloads the Record cache from the configured Proxy using the configured Reader and
13239 * the options from the last load operation performed.
13240 * @param {Object} options (optional) An object containing properties which may override the options
13241 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13242 * the most recently used options are reused).
13244 reload : function(options){
13245 this.load(Roo.applyIf(options||{}, this.lastOptions));
13249 // Called as a callback by the Reader during a load operation.
13250 loadRecords : function(o, options, success){
13251 if(!o || success === false){
13252 if(success !== false){
13253 this.fireEvent("load", this, [], options, o);
13255 if(options.callback){
13256 options.callback.call(options.scope || this, [], options, false);
13260 // if data returned failure - throw an exception.
13261 if (o.success === false) {
13262 // show a message if no listener is registered.
13263 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13264 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13266 // loadmask wil be hooked into this..
13267 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13270 var r = o.records, t = o.totalRecords || r.length;
13272 this.fireEvent("beforeloadadd", this, r, options, o);
13274 if(!options || options.add !== true){
13275 if(this.pruneModifiedRecords){
13276 this.modified = [];
13278 for(var i = 0, len = r.length; i < len; i++){
13282 this.data = this.snapshot;
13283 delete this.snapshot;
13286 this.data.addAll(r);
13287 this.totalLength = t;
13289 this.fireEvent("datachanged", this);
13291 this.totalLength = Math.max(t, this.data.length+r.length);
13295 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13297 var e = new Roo.data.Record({});
13299 e.set(this.parent.displayField, this.parent.emptyTitle);
13300 e.set(this.parent.valueField, '');
13305 this.fireEvent("load", this, r, options, o);
13306 if(options.callback){
13307 options.callback.call(options.scope || this, r, options, true);
13313 * Loads data from a passed data block. A Reader which understands the format of the data
13314 * must have been configured in the constructor.
13315 * @param {Object} data The data block from which to read the Records. The format of the data expected
13316 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13317 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13319 loadData : function(o, append){
13320 var r = this.reader.readRecords(o);
13321 this.loadRecords(r, {add: append}, true);
13325 * using 'cn' the nested child reader read the child array into it's child stores.
13326 * @param {Object} rec The record with a 'children array
13328 loadDataFromChildren : function(rec)
13330 this.loadData(this.reader.toLoadData(rec));
13335 * Gets the number of cached records.
13337 * <em>If using paging, this may not be the total size of the dataset. If the data object
13338 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13339 * the data set size</em>
13341 getCount : function(){
13342 return this.data.length || 0;
13346 * Gets the total number of records in the dataset as returned by the server.
13348 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13349 * the dataset size</em>
13351 getTotalCount : function(){
13352 return this.totalLength || 0;
13356 * Returns the sort state of the Store as an object with two properties:
13358 field {String} The name of the field by which the Records are sorted
13359 direction {String} The sort order, "ASC" or "DESC"
13362 getSortState : function(){
13363 return this.sortInfo;
13367 applySort : function(){
13368 if(this.sortInfo && !this.remoteSort){
13369 var s = this.sortInfo, f = s.field;
13370 var st = this.fields.get(f).sortType;
13371 var fn = function(r1, r2){
13372 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13373 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13375 this.data.sort(s.direction, fn);
13376 if(this.snapshot && this.snapshot != this.data){
13377 this.snapshot.sort(s.direction, fn);
13383 * Sets the default sort column and order to be used by the next load operation.
13384 * @param {String} fieldName The name of the field to sort by.
13385 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13387 setDefaultSort : function(field, dir){
13388 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13392 * Sort the Records.
13393 * If remote sorting is used, the sort is performed on the server, and the cache is
13394 * reloaded. If local sorting is used, the cache is sorted internally.
13395 * @param {String} fieldName The name of the field to sort by.
13396 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13398 sort : function(fieldName, dir){
13399 var f = this.fields.get(fieldName);
13401 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13403 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13404 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13409 this.sortToggle[f.name] = dir;
13410 this.sortInfo = {field: f.name, direction: dir};
13411 if(!this.remoteSort){
13413 this.fireEvent("datachanged", this);
13415 this.load(this.lastOptions);
13420 * Calls the specified function for each of the Records in the cache.
13421 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13422 * Returning <em>false</em> aborts and exits the iteration.
13423 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13425 each : function(fn, scope){
13426 this.data.each(fn, scope);
13430 * Gets all records modified since the last commit. Modified records are persisted across load operations
13431 * (e.g., during paging).
13432 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13434 getModifiedRecords : function(){
13435 return this.modified;
13439 createFilterFn : function(property, value, anyMatch){
13440 if(!value.exec){ // not a regex
13441 value = String(value);
13442 if(value.length == 0){
13445 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13447 return function(r){
13448 return value.test(r.data[property]);
13453 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13454 * @param {String} property A field on your records
13455 * @param {Number} start The record index to start at (defaults to 0)
13456 * @param {Number} end The last record index to include (defaults to length - 1)
13457 * @return {Number} The sum
13459 sum : function(property, start, end){
13460 var rs = this.data.items, v = 0;
13461 start = start || 0;
13462 end = (end || end === 0) ? end : rs.length-1;
13464 for(var i = start; i <= end; i++){
13465 v += (rs[i].data[property] || 0);
13471 * Filter the records by a specified property.
13472 * @param {String} field A field on your records
13473 * @param {String/RegExp} value Either a string that the field
13474 * should start with or a RegExp to test against the field
13475 * @param {Boolean} anyMatch True to match any part not just the beginning
13477 filter : function(property, value, anyMatch){
13478 var fn = this.createFilterFn(property, value, anyMatch);
13479 return fn ? this.filterBy(fn) : this.clearFilter();
13483 * Filter by a function. The specified function will be called with each
13484 * record in this data source. If the function returns true the record is included,
13485 * otherwise it is filtered.
13486 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13487 * @param {Object} scope (optional) The scope of the function (defaults to this)
13489 filterBy : function(fn, scope){
13490 this.snapshot = this.snapshot || this.data;
13491 this.data = this.queryBy(fn, scope||this);
13492 this.fireEvent("datachanged", this);
13496 * Query the records by a specified property.
13497 * @param {String} field A field on your records
13498 * @param {String/RegExp} value Either a string that the field
13499 * should start with or a RegExp to test against the field
13500 * @param {Boolean} anyMatch True to match any part not just the beginning
13501 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13503 query : function(property, value, anyMatch){
13504 var fn = this.createFilterFn(property, value, anyMatch);
13505 return fn ? this.queryBy(fn) : this.data.clone();
13509 * Query by a function. The specified function will be called with each
13510 * record in this data source. If the function returns true the record is included
13512 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13513 * @param {Object} scope (optional) The scope of the function (defaults to this)
13514 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13516 queryBy : function(fn, scope){
13517 var data = this.snapshot || this.data;
13518 return data.filterBy(fn, scope||this);
13522 * Collects unique values for a particular dataIndex from this store.
13523 * @param {String} dataIndex The property to collect
13524 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13525 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13526 * @return {Array} An array of the unique values
13528 collect : function(dataIndex, allowNull, bypassFilter){
13529 var d = (bypassFilter === true && this.snapshot) ?
13530 this.snapshot.items : this.data.items;
13531 var v, sv, r = [], l = {};
13532 for(var i = 0, len = d.length; i < len; i++){
13533 v = d[i].data[dataIndex];
13535 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13544 * Revert to a view of the Record cache with no filtering applied.
13545 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13547 clearFilter : function(suppressEvent){
13548 if(this.snapshot && this.snapshot != this.data){
13549 this.data = this.snapshot;
13550 delete this.snapshot;
13551 if(suppressEvent !== true){
13552 this.fireEvent("datachanged", this);
13558 afterEdit : function(record){
13559 if(this.modified.indexOf(record) == -1){
13560 this.modified.push(record);
13562 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13566 afterReject : function(record){
13567 this.modified.remove(record);
13568 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13572 afterCommit : function(record){
13573 this.modified.remove(record);
13574 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13578 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13579 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13581 commitChanges : function(){
13582 var m = this.modified.slice(0);
13583 this.modified = [];
13584 for(var i = 0, len = m.length; i < len; i++){
13590 * Cancel outstanding changes on all changed records.
13592 rejectChanges : function(){
13593 var m = this.modified.slice(0);
13594 this.modified = [];
13595 for(var i = 0, len = m.length; i < len; i++){
13600 onMetaChange : function(meta, rtype, o){
13601 this.recordType = rtype;
13602 this.fields = rtype.prototype.fields;
13603 delete this.snapshot;
13604 this.sortInfo = meta.sortInfo || this.sortInfo;
13605 this.modified = [];
13606 this.fireEvent('metachange', this, this.reader.meta);
13609 moveIndex : function(data, type)
13611 var index = this.indexOf(data);
13613 var newIndex = index + type;
13617 this.insert(newIndex, data);
13622 * Ext JS Library 1.1.1
13623 * Copyright(c) 2006-2007, Ext JS, LLC.
13625 * Originally Released Under LGPL - original licence link has changed is not relivant.
13628 * <script type="text/javascript">
13632 * @class Roo.data.SimpleStore
13633 * @extends Roo.data.Store
13634 * Small helper class to make creating Stores from Array data easier.
13635 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13636 * @cfg {Array} fields An array of field definition objects, or field name strings.
13637 * @cfg {Object} an existing reader (eg. copied from another store)
13638 * @cfg {Array} data The multi-dimensional array of data
13640 * @param {Object} config
13642 Roo.data.SimpleStore = function(config)
13644 Roo.data.SimpleStore.superclass.constructor.call(this, {
13646 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13649 Roo.data.Record.create(config.fields)
13651 proxy : new Roo.data.MemoryProxy(config.data)
13655 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13657 * Ext JS Library 1.1.1
13658 * Copyright(c) 2006-2007, Ext JS, LLC.
13660 * Originally Released Under LGPL - original licence link has changed is not relivant.
13663 * <script type="text/javascript">
13668 * @extends Roo.data.Store
13669 * @class Roo.data.JsonStore
13670 * Small helper class to make creating Stores for JSON data easier. <br/>
13672 var store = new Roo.data.JsonStore({
13673 url: 'get-images.php',
13675 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13678 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13679 * JsonReader and HttpProxy (unless inline data is provided).</b>
13680 * @cfg {Array} fields An array of field definition objects, or field name strings.
13682 * @param {Object} config
13684 Roo.data.JsonStore = function(c){
13685 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13686 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13687 reader: new Roo.data.JsonReader(c, c.fields)
13690 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13692 * Ext JS Library 1.1.1
13693 * Copyright(c) 2006-2007, Ext JS, LLC.
13695 * Originally Released Under LGPL - original licence link has changed is not relivant.
13698 * <script type="text/javascript">
13702 Roo.data.Field = function(config){
13703 if(typeof config == "string"){
13704 config = {name: config};
13706 Roo.apply(this, config);
13709 this.type = "auto";
13712 var st = Roo.data.SortTypes;
13713 // named sortTypes are supported, here we look them up
13714 if(typeof this.sortType == "string"){
13715 this.sortType = st[this.sortType];
13718 // set default sortType for strings and dates
13719 if(!this.sortType){
13722 this.sortType = st.asUCString;
13725 this.sortType = st.asDate;
13728 this.sortType = st.none;
13733 var stripRe = /[\$,%]/g;
13735 // prebuilt conversion function for this field, instead of
13736 // switching every time we're reading a value
13738 var cv, dateFormat = this.dateFormat;
13743 cv = function(v){ return v; };
13746 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13750 return v !== undefined && v !== null && v !== '' ?
13751 parseInt(String(v).replace(stripRe, ""), 10) : '';
13756 return v !== undefined && v !== null && v !== '' ?
13757 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13762 cv = function(v){ return v === true || v === "true" || v == 1; };
13769 if(v instanceof Date){
13773 if(dateFormat == "timestamp"){
13774 return new Date(v*1000);
13776 return Date.parseDate(v, dateFormat);
13778 var parsed = Date.parse(v);
13779 return parsed ? new Date(parsed) : null;
13788 Roo.data.Field.prototype = {
13796 * Ext JS Library 1.1.1
13797 * Copyright(c) 2006-2007, Ext JS, LLC.
13799 * Originally Released Under LGPL - original licence link has changed is not relivant.
13802 * <script type="text/javascript">
13805 // Base class for reading structured data from a data source. This class is intended to be
13806 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13809 * @class Roo.data.DataReader
13810 * Base class for reading structured data from a data source. This class is intended to be
13811 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13814 Roo.data.DataReader = function(meta, recordType){
13818 this.recordType = recordType instanceof Array ?
13819 Roo.data.Record.create(recordType) : recordType;
13822 Roo.data.DataReader.prototype = {
13825 readerType : 'Data',
13827 * Create an empty record
13828 * @param {Object} data (optional) - overlay some values
13829 * @return {Roo.data.Record} record created.
13831 newRow : function(d) {
13833 this.recordType.prototype.fields.each(function(c) {
13835 case 'int' : da[c.name] = 0; break;
13836 case 'date' : da[c.name] = new Date(); break;
13837 case 'float' : da[c.name] = 0.0; break;
13838 case 'boolean' : da[c.name] = false; break;
13839 default : da[c.name] = ""; break;
13843 return new this.recordType(Roo.apply(da, d));
13849 * Ext JS Library 1.1.1
13850 * Copyright(c) 2006-2007, Ext JS, LLC.
13852 * Originally Released Under LGPL - original licence link has changed is not relivant.
13855 * <script type="text/javascript">
13859 * @class Roo.data.DataProxy
13860 * @extends Roo.data.Observable
13861 * This class is an abstract base class for implementations which provide retrieval of
13862 * unformatted data objects.<br>
13864 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13865 * (of the appropriate type which knows how to parse the data object) to provide a block of
13866 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13868 * Custom implementations must implement the load method as described in
13869 * {@link Roo.data.HttpProxy#load}.
13871 Roo.data.DataProxy = function(){
13874 * @event beforeload
13875 * Fires before a network request is made to retrieve a data object.
13876 * @param {Object} This DataProxy object.
13877 * @param {Object} params The params parameter to the load function.
13882 * Fires before the load method's callback is called.
13883 * @param {Object} This DataProxy object.
13884 * @param {Object} o The data object.
13885 * @param {Object} arg The callback argument object passed to the load function.
13889 * @event loadexception
13890 * Fires if an Exception occurs during data retrieval.
13891 * @param {Object} This DataProxy object.
13892 * @param {Object} o The data object.
13893 * @param {Object} arg The callback argument object passed to the load function.
13894 * @param {Object} e The Exception.
13896 loadexception : true
13898 Roo.data.DataProxy.superclass.constructor.call(this);
13901 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13904 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13908 * Ext JS Library 1.1.1
13909 * Copyright(c) 2006-2007, Ext JS, LLC.
13911 * Originally Released Under LGPL - original licence link has changed is not relivant.
13914 * <script type="text/javascript">
13917 * @class Roo.data.MemoryProxy
13918 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13919 * to the Reader when its load method is called.
13921 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13923 Roo.data.MemoryProxy = function(data){
13927 Roo.data.MemoryProxy.superclass.constructor.call(this);
13931 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13934 * Load data from the requested source (in this case an in-memory
13935 * data object passed to the constructor), read the data object into
13936 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13937 * process that block using the passed callback.
13938 * @param {Object} params This parameter is not used by the MemoryProxy class.
13939 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13940 * object into a block of Roo.data.Records.
13941 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13942 * The function must be passed <ul>
13943 * <li>The Record block object</li>
13944 * <li>The "arg" argument from the load function</li>
13945 * <li>A boolean success indicator</li>
13947 * @param {Object} scope The scope in which to call the callback
13948 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13950 load : function(params, reader, callback, scope, arg){
13951 params = params || {};
13954 result = reader.readRecords(params.data ? params.data :this.data);
13956 this.fireEvent("loadexception", this, arg, null, e);
13957 callback.call(scope, null, arg, false);
13960 callback.call(scope, result, arg, true);
13964 update : function(params, records){
13969 * Ext JS Library 1.1.1
13970 * Copyright(c) 2006-2007, Ext JS, LLC.
13972 * Originally Released Under LGPL - original licence link has changed is not relivant.
13975 * <script type="text/javascript">
13978 * @class Roo.data.HttpProxy
13979 * @extends Roo.data.DataProxy
13980 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13981 * configured to reference a certain URL.<br><br>
13983 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13984 * from which the running page was served.<br><br>
13986 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13988 * Be aware that to enable the browser to parse an XML document, the server must set
13989 * the Content-Type header in the HTTP response to "text/xml".
13991 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13992 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
13993 * will be used to make the request.
13995 Roo.data.HttpProxy = function(conn){
13996 Roo.data.HttpProxy.superclass.constructor.call(this);
13997 // is conn a conn config or a real conn?
13999 this.useAjax = !conn || !conn.events;
14003 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14004 // thse are take from connection...
14007 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14010 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14011 * extra parameters to each request made by this object. (defaults to undefined)
14014 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14015 * to each request made by this object. (defaults to undefined)
14018 * @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)
14021 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14024 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14030 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14034 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14035 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14036 * a finer-grained basis than the DataProxy events.
14038 getConnection : function(){
14039 return this.useAjax ? Roo.Ajax : this.conn;
14043 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14044 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14045 * process that block using the passed callback.
14046 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14047 * for the request to the remote server.
14048 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14049 * object into a block of Roo.data.Records.
14050 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14051 * The function must be passed <ul>
14052 * <li>The Record block object</li>
14053 * <li>The "arg" argument from the load function</li>
14054 * <li>A boolean success indicator</li>
14056 * @param {Object} scope The scope in which to call the callback
14057 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14059 load : function(params, reader, callback, scope, arg){
14060 if(this.fireEvent("beforeload", this, params) !== false){
14062 params : params || {},
14064 callback : callback,
14069 callback : this.loadResponse,
14073 Roo.applyIf(o, this.conn);
14074 if(this.activeRequest){
14075 Roo.Ajax.abort(this.activeRequest);
14077 this.activeRequest = Roo.Ajax.request(o);
14079 this.conn.request(o);
14082 callback.call(scope||this, null, arg, false);
14087 loadResponse : function(o, success, response){
14088 delete this.activeRequest;
14090 this.fireEvent("loadexception", this, o, response);
14091 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14096 result = o.reader.read(response);
14098 this.fireEvent("loadexception", this, o, response, e);
14099 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14103 this.fireEvent("load", this, o, o.request.arg);
14104 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14108 update : function(dataSet){
14113 updateResponse : function(dataSet){
14118 * Ext JS Library 1.1.1
14119 * Copyright(c) 2006-2007, Ext JS, LLC.
14121 * Originally Released Under LGPL - original licence link has changed is not relivant.
14124 * <script type="text/javascript">
14128 * @class Roo.data.ScriptTagProxy
14129 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14130 * other than the originating domain of the running page.<br><br>
14132 * <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
14133 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14135 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14136 * source code that is used as the source inside a <script> tag.<br><br>
14138 * In order for the browser to process the returned data, the server must wrap the data object
14139 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14140 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14141 * depending on whether the callback name was passed:
14144 boolean scriptTag = false;
14145 String cb = request.getParameter("callback");
14148 response.setContentType("text/javascript");
14150 response.setContentType("application/x-json");
14152 Writer out = response.getWriter();
14154 out.write(cb + "(");
14156 out.print(dataBlock.toJsonString());
14163 * @param {Object} config A configuration object.
14165 Roo.data.ScriptTagProxy = function(config){
14166 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14167 Roo.apply(this, config);
14168 this.head = document.getElementsByTagName("head")[0];
14171 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14173 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14175 * @cfg {String} url The URL from which to request the data object.
14178 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14182 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14183 * the server the name of the callback function set up by the load call to process the returned data object.
14184 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14185 * javascript output which calls this named function passing the data object as its only parameter.
14187 callbackParam : "callback",
14189 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14190 * name to the request.
14195 * Load data from the configured URL, read the data object into
14196 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14197 * process that block using the passed callback.
14198 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14199 * for the request to the remote server.
14200 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14201 * object into a block of Roo.data.Records.
14202 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14203 * The function must be passed <ul>
14204 * <li>The Record block object</li>
14205 * <li>The "arg" argument from the load function</li>
14206 * <li>A boolean success indicator</li>
14208 * @param {Object} scope The scope in which to call the callback
14209 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14211 load : function(params, reader, callback, scope, arg){
14212 if(this.fireEvent("beforeload", this, params) !== false){
14214 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14216 var url = this.url;
14217 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14219 url += "&_dc=" + (new Date().getTime());
14221 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14224 cb : "stcCallback"+transId,
14225 scriptId : "stcScript"+transId,
14229 callback : callback,
14235 window[trans.cb] = function(o){
14236 conn.handleResponse(o, trans);
14239 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14241 if(this.autoAbort !== false){
14245 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14247 var script = document.createElement("script");
14248 script.setAttribute("src", url);
14249 script.setAttribute("type", "text/javascript");
14250 script.setAttribute("id", trans.scriptId);
14251 this.head.appendChild(script);
14253 this.trans = trans;
14255 callback.call(scope||this, null, arg, false);
14260 isLoading : function(){
14261 return this.trans ? true : false;
14265 * Abort the current server request.
14267 abort : function(){
14268 if(this.isLoading()){
14269 this.destroyTrans(this.trans);
14274 destroyTrans : function(trans, isLoaded){
14275 this.head.removeChild(document.getElementById(trans.scriptId));
14276 clearTimeout(trans.timeoutId);
14278 window[trans.cb] = undefined;
14280 delete window[trans.cb];
14283 // if hasn't been loaded, wait for load to remove it to prevent script error
14284 window[trans.cb] = function(){
14285 window[trans.cb] = undefined;
14287 delete window[trans.cb];
14294 handleResponse : function(o, trans){
14295 this.trans = false;
14296 this.destroyTrans(trans, true);
14299 result = trans.reader.readRecords(o);
14301 this.fireEvent("loadexception", this, o, trans.arg, e);
14302 trans.callback.call(trans.scope||window, null, trans.arg, false);
14305 this.fireEvent("load", this, o, trans.arg);
14306 trans.callback.call(trans.scope||window, result, trans.arg, true);
14310 handleFailure : function(trans){
14311 this.trans = false;
14312 this.destroyTrans(trans, false);
14313 this.fireEvent("loadexception", this, null, trans.arg);
14314 trans.callback.call(trans.scope||window, null, trans.arg, false);
14318 * Ext JS Library 1.1.1
14319 * Copyright(c) 2006-2007, Ext JS, LLC.
14321 * Originally Released Under LGPL - original licence link has changed is not relivant.
14324 * <script type="text/javascript">
14328 * @class Roo.data.JsonReader
14329 * @extends Roo.data.DataReader
14330 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14331 * based on mappings in a provided Roo.data.Record constructor.
14333 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14334 * in the reply previously.
14339 var RecordDef = Roo.data.Record.create([
14340 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14341 {name: 'occupation'} // This field will use "occupation" as the mapping.
14343 var myReader = new Roo.data.JsonReader({
14344 totalProperty: "results", // The property which contains the total dataset size (optional)
14345 root: "rows", // The property which contains an Array of row objects
14346 id: "id" // The property within each row object that provides an ID for the record (optional)
14350 * This would consume a JSON file like this:
14352 { 'results': 2, 'rows': [
14353 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14354 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14357 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14358 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14359 * paged from the remote server.
14360 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14361 * @cfg {String} root name of the property which contains the Array of row objects.
14362 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14363 * @cfg {Array} fields Array of field definition objects
14365 * Create a new JsonReader
14366 * @param {Object} meta Metadata configuration options
14367 * @param {Object} recordType Either an Array of field definition objects,
14368 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14370 Roo.data.JsonReader = function(meta, recordType){
14373 // set some defaults:
14374 Roo.applyIf(meta, {
14375 totalProperty: 'total',
14376 successProperty : 'success',
14381 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14383 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14385 readerType : 'Json',
14388 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14389 * Used by Store query builder to append _requestMeta to params.
14392 metaFromRemote : false,
14394 * This method is only used by a DataProxy which has retrieved data from a remote server.
14395 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14396 * @return {Object} data A data block which is used by an Roo.data.Store object as
14397 * a cache of Roo.data.Records.
14399 read : function(response){
14400 var json = response.responseText;
14402 var o = /* eval:var:o */ eval("("+json+")");
14404 throw {message: "JsonReader.read: Json object not found"};
14410 this.metaFromRemote = true;
14411 this.meta = o.metaData;
14412 this.recordType = Roo.data.Record.create(o.metaData.fields);
14413 this.onMetaChange(this.meta, this.recordType, o);
14415 return this.readRecords(o);
14418 // private function a store will implement
14419 onMetaChange : function(meta, recordType, o){
14426 simpleAccess: function(obj, subsc) {
14433 getJsonAccessor: function(){
14435 return function(expr) {
14437 return(re.test(expr))
14438 ? new Function("obj", "return obj." + expr)
14443 return Roo.emptyFn;
14448 * Create a data block containing Roo.data.Records from an XML document.
14449 * @param {Object} o An object which contains an Array of row objects in the property specified
14450 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14451 * which contains the total size of the dataset.
14452 * @return {Object} data A data block which is used by an Roo.data.Store object as
14453 * a cache of Roo.data.Records.
14455 readRecords : function(o){
14457 * After any data loads, the raw JSON data is available for further custom processing.
14461 var s = this.meta, Record = this.recordType,
14462 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14464 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14466 if(s.totalProperty) {
14467 this.getTotal = this.getJsonAccessor(s.totalProperty);
14469 if(s.successProperty) {
14470 this.getSuccess = this.getJsonAccessor(s.successProperty);
14472 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14474 var g = this.getJsonAccessor(s.id);
14475 this.getId = function(rec) {
14477 return (r === undefined || r === "") ? null : r;
14480 this.getId = function(){return null;};
14483 for(var jj = 0; jj < fl; jj++){
14485 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14486 this.ef[jj] = this.getJsonAccessor(map);
14490 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14491 if(s.totalProperty){
14492 var vt = parseInt(this.getTotal(o), 10);
14497 if(s.successProperty){
14498 var vs = this.getSuccess(o);
14499 if(vs === false || vs === 'false'){
14504 for(var i = 0; i < c; i++){
14507 var id = this.getId(n);
14508 for(var j = 0; j < fl; j++){
14510 var v = this.ef[j](n);
14512 Roo.log('missing convert for ' + f.name);
14516 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14518 var record = new Record(values, id);
14520 records[i] = record;
14526 totalRecords : totalRecords
14529 // used when loading children.. @see loadDataFromChildren
14530 toLoadData: function(rec)
14532 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14533 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14534 return { data : data, total : data.length };
14539 * Ext JS Library 1.1.1
14540 * Copyright(c) 2006-2007, Ext JS, LLC.
14542 * Originally Released Under LGPL - original licence link has changed is not relivant.
14545 * <script type="text/javascript">
14549 * @class Roo.data.ArrayReader
14550 * @extends Roo.data.DataReader
14551 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14552 * Each element of that Array represents a row of data fields. The
14553 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14554 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14558 var RecordDef = Roo.data.Record.create([
14559 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14560 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14562 var myReader = new Roo.data.ArrayReader({
14563 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14567 * This would consume an Array like this:
14569 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14573 * Create a new JsonReader
14574 * @param {Object} meta Metadata configuration options.
14575 * @param {Object|Array} recordType Either an Array of field definition objects
14577 * @cfg {Array} fields Array of field definition objects
14578 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14579 * as specified to {@link Roo.data.Record#create},
14580 * or an {@link Roo.data.Record} object
14583 * created using {@link Roo.data.Record#create}.
14585 Roo.data.ArrayReader = function(meta, recordType)
14587 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14590 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14593 * Create a data block containing Roo.data.Records from an XML document.
14594 * @param {Object} o An Array of row objects which represents the dataset.
14595 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14596 * a cache of Roo.data.Records.
14598 readRecords : function(o)
14600 var sid = this.meta ? this.meta.id : null;
14601 var recordType = this.recordType, fields = recordType.prototype.fields;
14604 for(var i = 0; i < root.length; i++){
14607 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14608 for(var j = 0, jlen = fields.length; j < jlen; j++){
14609 var f = fields.items[j];
14610 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14611 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14613 values[f.name] = v;
14615 var record = new recordType(values, id);
14617 records[records.length] = record;
14621 totalRecords : records.length
14624 // used when loading children.. @see loadDataFromChildren
14625 toLoadData: function(rec)
14627 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14628 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14639 * @class Roo.bootstrap.ComboBox
14640 * @extends Roo.bootstrap.TriggerField
14641 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14642 * @cfg {Boolean} append (true|false) default false
14643 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14644 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14645 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14646 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14647 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14648 * @cfg {Boolean} animate default true
14649 * @cfg {Boolean} emptyResultText only for touch device
14650 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14651 * @cfg {String} emptyTitle default ''
14653 * Create a new ComboBox.
14654 * @param {Object} config Configuration options
14656 Roo.bootstrap.ComboBox = function(config){
14657 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14661 * Fires when the dropdown list is expanded
14662 * @param {Roo.bootstrap.ComboBox} combo This combo box
14667 * Fires when the dropdown list is collapsed
14668 * @param {Roo.bootstrap.ComboBox} combo This combo box
14672 * @event beforeselect
14673 * Fires before a list item is selected. Return false to cancel the selection.
14674 * @param {Roo.bootstrap.ComboBox} combo This combo box
14675 * @param {Roo.data.Record} record The data record returned from the underlying store
14676 * @param {Number} index The index of the selected item in the dropdown list
14678 'beforeselect' : true,
14681 * Fires when a list item is selected
14682 * @param {Roo.bootstrap.ComboBox} combo This combo box
14683 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14684 * @param {Number} index The index of the selected item in the dropdown list
14688 * @event beforequery
14689 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14690 * The event object passed has these properties:
14691 * @param {Roo.bootstrap.ComboBox} combo This combo box
14692 * @param {String} query The query
14693 * @param {Boolean} forceAll true to force "all" query
14694 * @param {Boolean} cancel true to cancel the query
14695 * @param {Object} e The query event object
14697 'beforequery': true,
14700 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14701 * @param {Roo.bootstrap.ComboBox} combo This combo box
14706 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14707 * @param {Roo.bootstrap.ComboBox} combo This combo box
14708 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14713 * Fires when the remove value from the combobox array
14714 * @param {Roo.bootstrap.ComboBox} combo This combo box
14718 * @event afterremove
14719 * Fires when the remove value from the combobox array
14720 * @param {Roo.bootstrap.ComboBox} combo This combo box
14722 'afterremove' : true,
14724 * @event specialfilter
14725 * Fires when specialfilter
14726 * @param {Roo.bootstrap.ComboBox} combo This combo box
14728 'specialfilter' : true,
14731 * Fires when tick the element
14732 * @param {Roo.bootstrap.ComboBox} combo This combo box
14736 * @event touchviewdisplay
14737 * Fires when touch view require special display (default is using displayField)
14738 * @param {Roo.bootstrap.ComboBox} combo This combo box
14739 * @param {Object} cfg set html .
14741 'touchviewdisplay' : true
14746 this.tickItems = [];
14748 this.selectedIndex = -1;
14749 if(this.mode == 'local'){
14750 if(config.queryDelay === undefined){
14751 this.queryDelay = 10;
14753 if(config.minChars === undefined){
14759 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14762 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14763 * rendering into an Roo.Editor, defaults to false)
14766 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14767 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14770 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14773 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14774 * the dropdown list (defaults to undefined, with no header element)
14778 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14782 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14784 listWidth: undefined,
14786 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14787 * mode = 'remote' or 'text' if mode = 'local')
14789 displayField: undefined,
14792 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14793 * mode = 'remote' or 'value' if mode = 'local').
14794 * Note: use of a valueField requires the user make a selection
14795 * in order for a value to be mapped.
14797 valueField: undefined,
14799 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14804 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14805 * field's data value (defaults to the underlying DOM element's name)
14807 hiddenName: undefined,
14809 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14813 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14815 selectedClass: 'active',
14818 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14822 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14823 * anchor positions (defaults to 'tl-bl')
14825 listAlign: 'tl-bl?',
14827 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14831 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14832 * query specified by the allQuery config option (defaults to 'query')
14834 triggerAction: 'query',
14836 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14837 * (defaults to 4, does not apply if editable = false)
14841 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14842 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14846 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14847 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14851 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14852 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14856 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14857 * when editable = true (defaults to false)
14859 selectOnFocus:false,
14861 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14863 queryParam: 'query',
14865 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14866 * when mode = 'remote' (defaults to 'Loading...')
14868 loadingText: 'Loading...',
14870 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14874 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14878 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14879 * traditional select (defaults to true)
14883 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14887 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14891 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14892 * listWidth has a higher value)
14896 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14897 * allow the user to set arbitrary text into the field (defaults to false)
14899 forceSelection:false,
14901 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14902 * if typeAhead = true (defaults to 250)
14904 typeAheadDelay : 250,
14906 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14907 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14909 valueNotFoundText : undefined,
14911 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14913 blockFocus : false,
14916 * @cfg {Boolean} disableClear Disable showing of clear button.
14918 disableClear : false,
14920 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14922 alwaysQuery : false,
14925 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14930 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14932 invalidClass : "has-warning",
14935 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14937 validClass : "has-success",
14940 * @cfg {Boolean} specialFilter (true|false) special filter default false
14942 specialFilter : false,
14945 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14947 mobileTouchView : true,
14950 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14952 useNativeIOS : false,
14955 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14957 mobile_restrict_height : false,
14959 ios_options : false,
14971 btnPosition : 'right',
14972 triggerList : true,
14973 showToggleBtn : true,
14975 emptyResultText: 'Empty',
14976 triggerText : 'Select',
14979 // element that contains real text value.. (when hidden is used..)
14981 getAutoCreate : function()
14986 * Render classic select for iso
14989 if(Roo.isIOS && this.useNativeIOS){
14990 cfg = this.getAutoCreateNativeIOS();
14998 if(Roo.isTouch && this.mobileTouchView){
14999 cfg = this.getAutoCreateTouchView();
15006 if(!this.tickable){
15007 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15012 * ComboBox with tickable selections
15015 var align = this.labelAlign || this.parentLabelAlign();
15018 cls : 'form-group roo-combobox-tickable' //input-group
15021 var btn_text_select = '';
15022 var btn_text_done = '';
15023 var btn_text_cancel = '';
15025 if (this.btn_text_show) {
15026 btn_text_select = 'Select';
15027 btn_text_done = 'Done';
15028 btn_text_cancel = 'Cancel';
15033 cls : 'tickable-buttons',
15038 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15039 //html : this.triggerText
15040 html: btn_text_select
15046 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15048 html: btn_text_done
15054 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15056 html: btn_text_cancel
15062 buttons.cn.unshift({
15064 cls: 'roo-select2-search-field-input'
15070 Roo.each(buttons.cn, function(c){
15072 c.cls += ' btn-' + _this.size;
15075 if (_this.disabled) {
15082 style : 'display: contents',
15087 cls: 'form-hidden-field'
15091 cls: 'roo-select2-choices',
15095 cls: 'roo-select2-search-field',
15106 cls: 'roo-select2-container input-group roo-select2-container-multi',
15112 // cls: 'typeahead typeahead-long dropdown-menu',
15113 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15118 if(this.hasFeedback && !this.allowBlank){
15122 cls: 'glyphicon form-control-feedback'
15125 combobox.cn.push(feedback);
15132 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15133 tooltip : 'This field is required'
15135 if (Roo.bootstrap.version == 4) {
15138 style : 'display:none'
15141 if (align ==='left' && this.fieldLabel.length) {
15143 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15150 cls : 'control-label col-form-label',
15151 html : this.fieldLabel
15163 var labelCfg = cfg.cn[1];
15164 var contentCfg = cfg.cn[2];
15167 if(this.indicatorpos == 'right'){
15173 cls : 'control-label col-form-label',
15177 html : this.fieldLabel
15193 labelCfg = cfg.cn[0];
15194 contentCfg = cfg.cn[1];
15198 if(this.labelWidth > 12){
15199 labelCfg.style = "width: " + this.labelWidth + 'px';
15202 if(this.labelWidth < 13 && this.labelmd == 0){
15203 this.labelmd = this.labelWidth;
15206 if(this.labellg > 0){
15207 labelCfg.cls += ' col-lg-' + this.labellg;
15208 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15211 if(this.labelmd > 0){
15212 labelCfg.cls += ' col-md-' + this.labelmd;
15213 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15216 if(this.labelsm > 0){
15217 labelCfg.cls += ' col-sm-' + this.labelsm;
15218 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15221 if(this.labelxs > 0){
15222 labelCfg.cls += ' col-xs-' + this.labelxs;
15223 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15227 } else if ( this.fieldLabel.length) {
15228 // Roo.log(" label");
15233 //cls : 'input-group-addon',
15234 html : this.fieldLabel
15239 if(this.indicatorpos == 'right'){
15243 //cls : 'input-group-addon',
15244 html : this.fieldLabel
15254 // Roo.log(" no label && no align");
15261 ['xs','sm','md','lg'].map(function(size){
15262 if (settings[size]) {
15263 cfg.cls += ' col-' + size + '-' + settings[size];
15271 _initEventsCalled : false,
15274 initEvents: function()
15276 if (this._initEventsCalled) { // as we call render... prevent looping...
15279 this._initEventsCalled = true;
15282 throw "can not find store for combo";
15285 this.indicator = this.indicatorEl();
15287 this.store = Roo.factory(this.store, Roo.data);
15288 this.store.parent = this;
15290 // if we are building from html. then this element is so complex, that we can not really
15291 // use the rendered HTML.
15292 // so we have to trash and replace the previous code.
15293 if (Roo.XComponent.build_from_html) {
15294 // remove this element....
15295 var e = this.el.dom, k=0;
15296 while (e ) { e = e.previousSibling; ++k;}
15301 this.rendered = false;
15303 this.render(this.parent().getChildContainer(true), k);
15306 if(Roo.isIOS && this.useNativeIOS){
15307 this.initIOSView();
15315 if(Roo.isTouch && this.mobileTouchView){
15316 this.initTouchView();
15321 this.initTickableEvents();
15325 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15327 if(this.hiddenName){
15329 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15331 this.hiddenField.dom.value =
15332 this.hiddenValue !== undefined ? this.hiddenValue :
15333 this.value !== undefined ? this.value : '';
15335 // prevent input submission
15336 this.el.dom.removeAttribute('name');
15337 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15342 // this.el.dom.setAttribute('autocomplete', 'off');
15345 var cls = 'x-combo-list';
15347 //this.list = new Roo.Layer({
15348 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15354 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15355 _this.list.setWidth(lw);
15358 this.list.on('mouseover', this.onViewOver, this);
15359 this.list.on('mousemove', this.onViewMove, this);
15360 this.list.on('scroll', this.onViewScroll, this);
15363 this.list.swallowEvent('mousewheel');
15364 this.assetHeight = 0;
15367 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15368 this.assetHeight += this.header.getHeight();
15371 this.innerList = this.list.createChild({cls:cls+'-inner'});
15372 this.innerList.on('mouseover', this.onViewOver, this);
15373 this.innerList.on('mousemove', this.onViewMove, this);
15374 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15376 if(this.allowBlank && !this.pageSize && !this.disableClear){
15377 this.footer = this.list.createChild({cls:cls+'-ft'});
15378 this.pageTb = new Roo.Toolbar(this.footer);
15382 this.footer = this.list.createChild({cls:cls+'-ft'});
15383 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15384 {pageSize: this.pageSize});
15388 if (this.pageTb && this.allowBlank && !this.disableClear) {
15390 this.pageTb.add(new Roo.Toolbar.Fill(), {
15391 cls: 'x-btn-icon x-btn-clear',
15393 handler: function()
15396 _this.clearValue();
15397 _this.onSelect(false, -1);
15402 this.assetHeight += this.footer.getHeight();
15407 this.tpl = Roo.bootstrap.version == 4 ?
15408 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15409 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15412 this.view = new Roo.View(this.list, this.tpl, {
15413 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15415 //this.view.wrapEl.setDisplayed(false);
15416 this.view.on('click', this.onViewClick, this);
15419 this.store.on('beforeload', this.onBeforeLoad, this);
15420 this.store.on('load', this.onLoad, this);
15421 this.store.on('loadexception', this.onLoadException, this);
15423 if(this.resizable){
15424 this.resizer = new Roo.Resizable(this.list, {
15425 pinned:true, handles:'se'
15427 this.resizer.on('resize', function(r, w, h){
15428 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15429 this.listWidth = w;
15430 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15431 this.restrictHeight();
15433 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15436 if(!this.editable){
15437 this.editable = true;
15438 this.setEditable(false);
15443 if (typeof(this.events.add.listeners) != 'undefined') {
15445 this.addicon = this.wrap.createChild(
15446 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15448 this.addicon.on('click', function(e) {
15449 this.fireEvent('add', this);
15452 if (typeof(this.events.edit.listeners) != 'undefined') {
15454 this.editicon = this.wrap.createChild(
15455 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15456 if (this.addicon) {
15457 this.editicon.setStyle('margin-left', '40px');
15459 this.editicon.on('click', function(e) {
15461 // we fire even if inothing is selected..
15462 this.fireEvent('edit', this, this.lastData );
15468 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15469 "up" : function(e){
15470 this.inKeyMode = true;
15474 "down" : function(e){
15475 if(!this.isExpanded()){
15476 this.onTriggerClick();
15478 this.inKeyMode = true;
15483 "enter" : function(e){
15484 // this.onViewClick();
15488 if(this.fireEvent("specialkey", this, e)){
15489 this.onViewClick(false);
15495 "esc" : function(e){
15499 "tab" : function(e){
15502 if(this.fireEvent("specialkey", this, e)){
15503 this.onViewClick(false);
15511 doRelay : function(foo, bar, hname){
15512 if(hname == 'down' || this.scope.isExpanded()){
15513 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15522 this.queryDelay = Math.max(this.queryDelay || 10,
15523 this.mode == 'local' ? 10 : 250);
15526 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15528 if(this.typeAhead){
15529 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15531 if(this.editable !== false){
15532 this.inputEl().on("keyup", this.onKeyUp, this);
15534 if(this.forceSelection){
15535 this.inputEl().on('blur', this.doForce, this);
15539 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15540 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15544 initTickableEvents: function()
15548 if(this.hiddenName){
15550 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15552 this.hiddenField.dom.value =
15553 this.hiddenValue !== undefined ? this.hiddenValue :
15554 this.value !== undefined ? this.value : '';
15556 // prevent input submission
15557 this.el.dom.removeAttribute('name');
15558 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15563 // this.list = this.el.select('ul.dropdown-menu',true).first();
15565 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15566 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15567 if(this.triggerList){
15568 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15571 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15572 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15574 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15575 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15577 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15578 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15580 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15581 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15582 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15585 this.cancelBtn.hide();
15590 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15591 _this.list.setWidth(lw);
15594 this.list.on('mouseover', this.onViewOver, this);
15595 this.list.on('mousemove', this.onViewMove, this);
15597 this.list.on('scroll', this.onViewScroll, this);
15600 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15601 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15604 this.view = new Roo.View(this.list, this.tpl, {
15609 selectedClass: this.selectedClass
15612 //this.view.wrapEl.setDisplayed(false);
15613 this.view.on('click', this.onViewClick, this);
15617 this.store.on('beforeload', this.onBeforeLoad, this);
15618 this.store.on('load', this.onLoad, this);
15619 this.store.on('loadexception', this.onLoadException, this);
15622 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15623 "up" : function(e){
15624 this.inKeyMode = true;
15628 "down" : function(e){
15629 this.inKeyMode = true;
15633 "enter" : function(e){
15634 if(this.fireEvent("specialkey", this, e)){
15635 this.onViewClick(false);
15641 "esc" : function(e){
15642 this.onTickableFooterButtonClick(e, false, false);
15645 "tab" : function(e){
15646 this.fireEvent("specialkey", this, e);
15648 this.onTickableFooterButtonClick(e, false, false);
15655 doRelay : function(e, fn, key){
15656 if(this.scope.isExpanded()){
15657 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15666 this.queryDelay = Math.max(this.queryDelay || 10,
15667 this.mode == 'local' ? 10 : 250);
15670 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15672 if(this.typeAhead){
15673 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15676 if(this.editable !== false){
15677 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15680 this.indicator = this.indicatorEl();
15682 if(this.indicator){
15683 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15684 this.indicator.hide();
15689 onDestroy : function(){
15691 this.view.setStore(null);
15692 this.view.el.removeAllListeners();
15693 this.view.el.remove();
15694 this.view.purgeListeners();
15697 this.list.dom.innerHTML = '';
15701 this.store.un('beforeload', this.onBeforeLoad, this);
15702 this.store.un('load', this.onLoad, this);
15703 this.store.un('loadexception', this.onLoadException, this);
15705 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15709 fireKey : function(e){
15710 if(e.isNavKeyPress() && !this.list.isVisible()){
15711 this.fireEvent("specialkey", this, e);
15716 onResize: function(w, h){
15717 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15719 // if(typeof w != 'number'){
15720 // // we do not handle it!?!?
15723 // var tw = this.trigger.getWidth();
15724 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15725 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15727 // this.inputEl().setWidth( this.adjustWidth('input', x));
15729 // //this.trigger.setStyle('left', x+'px');
15731 // if(this.list && this.listWidth === undefined){
15732 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15733 // this.list.setWidth(lw);
15734 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15742 * Allow or prevent the user from directly editing the field text. If false is passed,
15743 * the user will only be able to select from the items defined in the dropdown list. This method
15744 * is the runtime equivalent of setting the 'editable' config option at config time.
15745 * @param {Boolean} value True to allow the user to directly edit the field text
15747 setEditable : function(value){
15748 if(value == this.editable){
15751 this.editable = value;
15753 this.inputEl().dom.setAttribute('readOnly', true);
15754 this.inputEl().on('mousedown', this.onTriggerClick, this);
15755 this.inputEl().addClass('x-combo-noedit');
15757 this.inputEl().dom.setAttribute('readOnly', false);
15758 this.inputEl().un('mousedown', this.onTriggerClick, this);
15759 this.inputEl().removeClass('x-combo-noedit');
15765 onBeforeLoad : function(combo,opts){
15766 if(!this.hasFocus){
15770 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15772 this.restrictHeight();
15773 this.selectedIndex = -1;
15777 onLoad : function(){
15779 this.hasQuery = false;
15781 if(!this.hasFocus){
15785 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15786 this.loading.hide();
15789 if(this.store.getCount() > 0){
15792 this.restrictHeight();
15793 if(this.lastQuery == this.allQuery){
15794 if(this.editable && !this.tickable){
15795 this.inputEl().dom.select();
15799 !this.selectByValue(this.value, true) &&
15802 !this.store.lastOptions ||
15803 typeof(this.store.lastOptions.add) == 'undefined' ||
15804 this.store.lastOptions.add != true
15807 this.select(0, true);
15810 if(this.autoFocus){
15813 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15814 this.taTask.delay(this.typeAheadDelay);
15818 this.onEmptyResults();
15824 onLoadException : function()
15826 this.hasQuery = false;
15828 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15829 this.loading.hide();
15832 if(this.tickable && this.editable){
15837 // only causes errors at present
15838 //Roo.log(this.store.reader.jsonData);
15839 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15841 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15847 onTypeAhead : function(){
15848 if(this.store.getCount() > 0){
15849 var r = this.store.getAt(0);
15850 var newValue = r.data[this.displayField];
15851 var len = newValue.length;
15852 var selStart = this.getRawValue().length;
15854 if(selStart != len){
15855 this.setRawValue(newValue);
15856 this.selectText(selStart, newValue.length);
15862 onSelect : function(record, index){
15864 if(this.fireEvent('beforeselect', this, record, index) !== false){
15866 this.setFromData(index > -1 ? record.data : false);
15869 this.fireEvent('select', this, record, index);
15874 * Returns the currently selected field value or empty string if no value is set.
15875 * @return {String} value The selected value
15877 getValue : function()
15879 if(Roo.isIOS && this.useNativeIOS){
15880 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15884 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15887 if(this.valueField){
15888 return typeof this.value != 'undefined' ? this.value : '';
15890 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15894 getRawValue : function()
15896 if(Roo.isIOS && this.useNativeIOS){
15897 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15900 var v = this.inputEl().getValue();
15906 * Clears any text/value currently set in the field
15908 clearValue : function(){
15910 if(this.hiddenField){
15911 this.hiddenField.dom.value = '';
15914 this.setRawValue('');
15915 this.lastSelectionText = '';
15916 this.lastData = false;
15918 var close = this.closeTriggerEl();
15929 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15930 * will be displayed in the field. If the value does not match the data value of an existing item,
15931 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15932 * Otherwise the field will be blank (although the value will still be set).
15933 * @param {String} value The value to match
15935 setValue : function(v)
15937 if(Roo.isIOS && this.useNativeIOS){
15938 this.setIOSValue(v);
15948 if(this.valueField){
15949 var r = this.findRecord(this.valueField, v);
15951 text = r.data[this.displayField];
15952 }else if(this.valueNotFoundText !== undefined){
15953 text = this.valueNotFoundText;
15956 this.lastSelectionText = text;
15957 if(this.hiddenField){
15958 this.hiddenField.dom.value = v;
15960 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15963 var close = this.closeTriggerEl();
15966 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15972 * @property {Object} the last set data for the element
15977 * Sets the value of the field based on a object which is related to the record format for the store.
15978 * @param {Object} value the value to set as. or false on reset?
15980 setFromData : function(o){
15987 var dv = ''; // display value
15988 var vv = ''; // value value..
15990 if (this.displayField) {
15991 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15993 // this is an error condition!!!
15994 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15997 if(this.valueField){
15998 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16001 var close = this.closeTriggerEl();
16004 if(dv.length || vv * 1 > 0){
16006 this.blockFocus=true;
16012 if(this.hiddenField){
16013 this.hiddenField.dom.value = vv;
16015 this.lastSelectionText = dv;
16016 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16020 // no hidden field.. - we store the value in 'value', but still display
16021 // display field!!!!
16022 this.lastSelectionText = dv;
16023 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16030 reset : function(){
16031 // overridden so that last data is reset..
16038 this.setValue(this.originalValue);
16039 //this.clearInvalid();
16040 this.lastData = false;
16042 this.view.clearSelections();
16048 findRecord : function(prop, value){
16050 if(this.store.getCount() > 0){
16051 this.store.each(function(r){
16052 if(r.data[prop] == value){
16062 getName: function()
16064 // returns hidden if it's set..
16065 if (!this.rendered) {return ''};
16066 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16070 onViewMove : function(e, t){
16071 this.inKeyMode = false;
16075 onViewOver : function(e, t){
16076 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16079 var item = this.view.findItemFromChild(t);
16082 var index = this.view.indexOf(item);
16083 this.select(index, false);
16088 onViewClick : function(view, doFocus, el, e)
16090 var index = this.view.getSelectedIndexes()[0];
16092 var r = this.store.getAt(index);
16096 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16103 Roo.each(this.tickItems, function(v,k){
16105 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16107 _this.tickItems.splice(k, 1);
16109 if(typeof(e) == 'undefined' && view == false){
16110 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16122 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16123 this.tickItems.push(r.data);
16126 if(typeof(e) == 'undefined' && view == false){
16127 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16134 this.onSelect(r, index);
16136 if(doFocus !== false && !this.blockFocus){
16137 this.inputEl().focus();
16142 restrictHeight : function(){
16143 //this.innerList.dom.style.height = '';
16144 //var inner = this.innerList.dom;
16145 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16146 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16147 //this.list.beginUpdate();
16148 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16149 this.list.alignTo(this.inputEl(), this.listAlign);
16150 this.list.alignTo(this.inputEl(), this.listAlign);
16151 //this.list.endUpdate();
16155 onEmptyResults : function(){
16157 if(this.tickable && this.editable){
16158 this.hasFocus = false;
16159 this.restrictHeight();
16167 * Returns true if the dropdown list is expanded, else false.
16169 isExpanded : function(){
16170 return this.list.isVisible();
16174 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16175 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16176 * @param {String} value The data value of the item to select
16177 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16178 * selected item if it is not currently in view (defaults to true)
16179 * @return {Boolean} True if the value matched an item in the list, else false
16181 selectByValue : function(v, scrollIntoView){
16182 if(v !== undefined && v !== null){
16183 var r = this.findRecord(this.valueField || this.displayField, v);
16185 this.select(this.store.indexOf(r), scrollIntoView);
16193 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16194 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16195 * @param {Number} index The zero-based index of the list item to select
16196 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16197 * selected item if it is not currently in view (defaults to true)
16199 select : function(index, scrollIntoView){
16200 this.selectedIndex = index;
16201 this.view.select(index);
16202 if(scrollIntoView !== false){
16203 var el = this.view.getNode(index);
16205 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16208 this.list.scrollChildIntoView(el, false);
16214 selectNext : function(){
16215 var ct = this.store.getCount();
16217 if(this.selectedIndex == -1){
16219 }else if(this.selectedIndex < ct-1){
16220 this.select(this.selectedIndex+1);
16226 selectPrev : function(){
16227 var ct = this.store.getCount();
16229 if(this.selectedIndex == -1){
16231 }else if(this.selectedIndex != 0){
16232 this.select(this.selectedIndex-1);
16238 onKeyUp : function(e){
16239 if(this.editable !== false && !e.isSpecialKey()){
16240 this.lastKey = e.getKey();
16241 this.dqTask.delay(this.queryDelay);
16246 validateBlur : function(){
16247 return !this.list || !this.list.isVisible();
16251 initQuery : function(){
16253 var v = this.getRawValue();
16255 if(this.tickable && this.editable){
16256 v = this.tickableInputEl().getValue();
16263 doForce : function(){
16264 if(this.inputEl().dom.value.length > 0){
16265 this.inputEl().dom.value =
16266 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16272 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16273 * query allowing the query action to be canceled if needed.
16274 * @param {String} query The SQL query to execute
16275 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16276 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16277 * saved in the current store (defaults to false)
16279 doQuery : function(q, forceAll){
16281 if(q === undefined || q === null){
16286 forceAll: forceAll,
16290 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16295 forceAll = qe.forceAll;
16296 if(forceAll === true || (q.length >= this.minChars)){
16298 this.hasQuery = true;
16300 if(this.lastQuery != q || this.alwaysQuery){
16301 this.lastQuery = q;
16302 if(this.mode == 'local'){
16303 this.selectedIndex = -1;
16305 this.store.clearFilter();
16308 if(this.specialFilter){
16309 this.fireEvent('specialfilter', this);
16314 this.store.filter(this.displayField, q);
16317 this.store.fireEvent("datachanged", this.store);
16324 this.store.baseParams[this.queryParam] = q;
16326 var options = {params : this.getParams(q)};
16329 options.add = true;
16330 options.params.start = this.page * this.pageSize;
16333 this.store.load(options);
16336 * this code will make the page width larger, at the beginning, the list not align correctly,
16337 * we should expand the list on onLoad
16338 * so command out it
16343 this.selectedIndex = -1;
16348 this.loadNext = false;
16352 getParams : function(q){
16354 //p[this.queryParam] = q;
16358 p.limit = this.pageSize;
16364 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16366 collapse : function(){
16367 if(!this.isExpanded()){
16373 this.hasFocus = false;
16377 this.cancelBtn.hide();
16378 this.trigger.show();
16381 this.tickableInputEl().dom.value = '';
16382 this.tickableInputEl().blur();
16387 Roo.get(document).un('mousedown', this.collapseIf, this);
16388 Roo.get(document).un('mousewheel', this.collapseIf, this);
16389 if (!this.editable) {
16390 Roo.get(document).un('keydown', this.listKeyPress, this);
16392 this.fireEvent('collapse', this);
16398 collapseIf : function(e){
16399 var in_combo = e.within(this.el);
16400 var in_list = e.within(this.list);
16401 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16403 if (in_combo || in_list || is_list) {
16404 //e.stopPropagation();
16409 this.onTickableFooterButtonClick(e, false, false);
16417 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16419 expand : function(){
16421 if(this.isExpanded() || !this.hasFocus){
16425 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16426 this.list.setWidth(lw);
16432 this.restrictHeight();
16436 this.tickItems = Roo.apply([], this.item);
16439 this.cancelBtn.show();
16440 this.trigger.hide();
16443 this.tickableInputEl().focus();
16448 Roo.get(document).on('mousedown', this.collapseIf, this);
16449 Roo.get(document).on('mousewheel', this.collapseIf, this);
16450 if (!this.editable) {
16451 Roo.get(document).on('keydown', this.listKeyPress, this);
16454 this.fireEvent('expand', this);
16458 // Implements the default empty TriggerField.onTriggerClick function
16459 onTriggerClick : function(e)
16461 Roo.log('trigger click');
16463 if(this.disabled || !this.triggerList){
16468 this.loadNext = false;
16470 if(this.isExpanded()){
16472 if (!this.blockFocus) {
16473 this.inputEl().focus();
16477 this.hasFocus = true;
16478 if(this.triggerAction == 'all') {
16479 this.doQuery(this.allQuery, true);
16481 this.doQuery(this.getRawValue());
16483 if (!this.blockFocus) {
16484 this.inputEl().focus();
16489 onTickableTriggerClick : function(e)
16496 this.loadNext = false;
16497 this.hasFocus = true;
16499 if(this.triggerAction == 'all') {
16500 this.doQuery(this.allQuery, true);
16502 this.doQuery(this.getRawValue());
16506 onSearchFieldClick : function(e)
16508 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16509 this.onTickableFooterButtonClick(e, false, false);
16513 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16518 this.loadNext = false;
16519 this.hasFocus = true;
16521 if(this.triggerAction == 'all') {
16522 this.doQuery(this.allQuery, true);
16524 this.doQuery(this.getRawValue());
16528 listKeyPress : function(e)
16530 //Roo.log('listkeypress');
16531 // scroll to first matching element based on key pres..
16532 if (e.isSpecialKey()) {
16535 var k = String.fromCharCode(e.getKey()).toUpperCase();
16538 var csel = this.view.getSelectedNodes();
16539 var cselitem = false;
16541 var ix = this.view.indexOf(csel[0]);
16542 cselitem = this.store.getAt(ix);
16543 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16549 this.store.each(function(v) {
16551 // start at existing selection.
16552 if (cselitem.id == v.id) {
16558 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16559 match = this.store.indexOf(v);
16565 if (match === false) {
16566 return true; // no more action?
16569 this.view.select(match);
16570 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16571 sn.scrollIntoView(sn.dom.parentNode, false);
16574 onViewScroll : function(e, t){
16576 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){
16580 this.hasQuery = true;
16582 this.loading = this.list.select('.loading', true).first();
16584 if(this.loading === null){
16585 this.list.createChild({
16587 cls: 'loading roo-select2-more-results roo-select2-active',
16588 html: 'Loading more results...'
16591 this.loading = this.list.select('.loading', true).first();
16593 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16595 this.loading.hide();
16598 this.loading.show();
16603 this.loadNext = true;
16605 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16610 addItem : function(o)
16612 var dv = ''; // display value
16614 if (this.displayField) {
16615 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16617 // this is an error condition!!!
16618 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16625 var choice = this.choices.createChild({
16627 cls: 'roo-select2-search-choice',
16636 cls: 'roo-select2-search-choice-close fa fa-times',
16641 }, this.searchField);
16643 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16645 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16653 this.inputEl().dom.value = '';
16658 onRemoveItem : function(e, _self, o)
16660 e.preventDefault();
16662 this.lastItem = Roo.apply([], this.item);
16664 var index = this.item.indexOf(o.data) * 1;
16667 Roo.log('not this item?!');
16671 this.item.splice(index, 1);
16676 this.fireEvent('remove', this, e);
16682 syncValue : function()
16684 if(!this.item.length){
16691 Roo.each(this.item, function(i){
16692 if(_this.valueField){
16693 value.push(i[_this.valueField]);
16700 this.value = value.join(',');
16702 if(this.hiddenField){
16703 this.hiddenField.dom.value = this.value;
16706 this.store.fireEvent("datachanged", this.store);
16711 clearItem : function()
16713 if(!this.multiple){
16719 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16727 if(this.tickable && !Roo.isTouch){
16728 this.view.refresh();
16732 inputEl: function ()
16734 if(Roo.isIOS && this.useNativeIOS){
16735 return this.el.select('select.roo-ios-select', true).first();
16738 if(Roo.isTouch && this.mobileTouchView){
16739 return this.el.select('input.form-control',true).first();
16743 return this.searchField;
16746 return this.el.select('input.form-control',true).first();
16749 onTickableFooterButtonClick : function(e, btn, el)
16751 e.preventDefault();
16753 this.lastItem = Roo.apply([], this.item);
16755 if(btn && btn.name == 'cancel'){
16756 this.tickItems = Roo.apply([], this.item);
16765 Roo.each(this.tickItems, function(o){
16773 validate : function()
16775 if(this.getVisibilityEl().hasClass('hidden')){
16779 var v = this.getRawValue();
16782 v = this.getValue();
16785 if(this.disabled || this.allowBlank || v.length){
16790 this.markInvalid();
16794 tickableInputEl : function()
16796 if(!this.tickable || !this.editable){
16797 return this.inputEl();
16800 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16804 getAutoCreateTouchView : function()
16809 cls: 'form-group' //input-group
16815 type : this.inputType,
16816 cls : 'form-control x-combo-noedit',
16817 autocomplete: 'new-password',
16818 placeholder : this.placeholder || '',
16823 input.name = this.name;
16827 input.cls += ' input-' + this.size;
16830 if (this.disabled) {
16831 input.disabled = true;
16842 inputblock.cls += ' input-group';
16844 inputblock.cn.unshift({
16846 cls : 'input-group-addon input-group-prepend input-group-text',
16851 if(this.removable && !this.multiple){
16852 inputblock.cls += ' roo-removable';
16854 inputblock.cn.push({
16857 cls : 'roo-combo-removable-btn close'
16861 if(this.hasFeedback && !this.allowBlank){
16863 inputblock.cls += ' has-feedback';
16865 inputblock.cn.push({
16867 cls: 'glyphicon form-control-feedback'
16874 inputblock.cls += (this.before) ? '' : ' input-group';
16876 inputblock.cn.push({
16878 cls : 'input-group-addon input-group-append input-group-text',
16884 var ibwrap = inputblock;
16889 cls: 'roo-select2-choices',
16893 cls: 'roo-select2-search-field',
16906 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16911 cls: 'form-hidden-field'
16917 if(!this.multiple && this.showToggleBtn){
16923 if (this.caret != false) {
16926 cls: 'fa fa-' + this.caret
16933 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16935 Roo.bootstrap.version == 3 ? caret : '',
16938 cls: 'combobox-clear',
16952 combobox.cls += ' roo-select2-container-multi';
16955 var align = this.labelAlign || this.parentLabelAlign();
16957 if (align ==='left' && this.fieldLabel.length) {
16962 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16963 tooltip : 'This field is required'
16967 cls : 'control-label col-form-label',
16968 html : this.fieldLabel
16979 var labelCfg = cfg.cn[1];
16980 var contentCfg = cfg.cn[2];
16983 if(this.indicatorpos == 'right'){
16988 cls : 'control-label col-form-label',
16992 html : this.fieldLabel
16996 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16997 tooltip : 'This field is required'
17010 labelCfg = cfg.cn[0];
17011 contentCfg = cfg.cn[1];
17016 if(this.labelWidth > 12){
17017 labelCfg.style = "width: " + this.labelWidth + 'px';
17020 if(this.labelWidth < 13 && this.labelmd == 0){
17021 this.labelmd = this.labelWidth;
17024 if(this.labellg > 0){
17025 labelCfg.cls += ' col-lg-' + this.labellg;
17026 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17029 if(this.labelmd > 0){
17030 labelCfg.cls += ' col-md-' + this.labelmd;
17031 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17034 if(this.labelsm > 0){
17035 labelCfg.cls += ' col-sm-' + this.labelsm;
17036 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17039 if(this.labelxs > 0){
17040 labelCfg.cls += ' col-xs-' + this.labelxs;
17041 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17045 } else if ( this.fieldLabel.length) {
17049 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17050 tooltip : 'This field is required'
17054 cls : 'control-label',
17055 html : this.fieldLabel
17066 if(this.indicatorpos == 'right'){
17070 cls : 'control-label',
17071 html : this.fieldLabel,
17075 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17076 tooltip : 'This field is required'
17093 var settings = this;
17095 ['xs','sm','md','lg'].map(function(size){
17096 if (settings[size]) {
17097 cfg.cls += ' col-' + size + '-' + settings[size];
17104 initTouchView : function()
17106 this.renderTouchView();
17108 this.touchViewEl.on('scroll', function(){
17109 this.el.dom.scrollTop = 0;
17112 this.originalValue = this.getValue();
17114 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17116 this.inputEl().on("click", this.showTouchView, this);
17117 if (this.triggerEl) {
17118 this.triggerEl.on("click", this.showTouchView, this);
17122 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17123 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17125 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17127 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17128 this.store.on('load', this.onTouchViewLoad, this);
17129 this.store.on('loadexception', this.onTouchViewLoadException, this);
17131 if(this.hiddenName){
17133 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17135 this.hiddenField.dom.value =
17136 this.hiddenValue !== undefined ? this.hiddenValue :
17137 this.value !== undefined ? this.value : '';
17139 this.el.dom.removeAttribute('name');
17140 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17144 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17145 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17148 if(this.removable && !this.multiple){
17149 var close = this.closeTriggerEl();
17151 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17152 close.on('click', this.removeBtnClick, this, close);
17156 * fix the bug in Safari iOS8
17158 this.inputEl().on("focus", function(e){
17159 document.activeElement.blur();
17162 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17169 renderTouchView : function()
17171 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17172 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17174 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17175 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17177 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17178 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17179 this.touchViewBodyEl.setStyle('overflow', 'auto');
17181 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17182 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17184 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17185 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17189 showTouchView : function()
17195 this.touchViewHeaderEl.hide();
17197 if(this.modalTitle.length){
17198 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17199 this.touchViewHeaderEl.show();
17202 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17203 this.touchViewEl.show();
17205 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17207 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17208 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17210 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17212 if(this.modalTitle.length){
17213 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17216 this.touchViewBodyEl.setHeight(bodyHeight);
17220 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17222 this.touchViewEl.addClass('in');
17225 if(this._touchViewMask){
17226 Roo.get(document.body).addClass("x-body-masked");
17227 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17228 this._touchViewMask.setStyle('z-index', 10000);
17229 this._touchViewMask.addClass('show');
17232 this.doTouchViewQuery();
17236 hideTouchView : function()
17238 this.touchViewEl.removeClass('in');
17242 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17244 this.touchViewEl.setStyle('display', 'none');
17247 if(this._touchViewMask){
17248 this._touchViewMask.removeClass('show');
17249 Roo.get(document.body).removeClass("x-body-masked");
17253 setTouchViewValue : function()
17260 Roo.each(this.tickItems, function(o){
17265 this.hideTouchView();
17268 doTouchViewQuery : function()
17277 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17281 if(!this.alwaysQuery || this.mode == 'local'){
17282 this.onTouchViewLoad();
17289 onTouchViewBeforeLoad : function(combo,opts)
17295 onTouchViewLoad : function()
17297 if(this.store.getCount() < 1){
17298 this.onTouchViewEmptyResults();
17302 this.clearTouchView();
17304 var rawValue = this.getRawValue();
17306 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17308 this.tickItems = [];
17310 this.store.data.each(function(d, rowIndex){
17311 var row = this.touchViewListGroup.createChild(template);
17313 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17314 row.addClass(d.data.cls);
17317 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17320 html : d.data[this.displayField]
17323 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17324 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17327 row.removeClass('selected');
17328 if(!this.multiple && this.valueField &&
17329 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17332 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17333 row.addClass('selected');
17336 if(this.multiple && this.valueField &&
17337 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17341 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17342 this.tickItems.push(d.data);
17345 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17349 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17351 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17353 if(this.modalTitle.length){
17354 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17357 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17359 if(this.mobile_restrict_height && listHeight < bodyHeight){
17360 this.touchViewBodyEl.setHeight(listHeight);
17365 if(firstChecked && listHeight > bodyHeight){
17366 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17371 onTouchViewLoadException : function()
17373 this.hideTouchView();
17376 onTouchViewEmptyResults : function()
17378 this.clearTouchView();
17380 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17382 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17386 clearTouchView : function()
17388 this.touchViewListGroup.dom.innerHTML = '';
17391 onTouchViewClick : function(e, el, o)
17393 e.preventDefault();
17396 var rowIndex = o.rowIndex;
17398 var r = this.store.getAt(rowIndex);
17400 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17402 if(!this.multiple){
17403 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17404 c.dom.removeAttribute('checked');
17407 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17409 this.setFromData(r.data);
17411 var close = this.closeTriggerEl();
17417 this.hideTouchView();
17419 this.fireEvent('select', this, r, rowIndex);
17424 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17425 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17426 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17430 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17431 this.addItem(r.data);
17432 this.tickItems.push(r.data);
17436 getAutoCreateNativeIOS : function()
17439 cls: 'form-group' //input-group,
17444 cls : 'roo-ios-select'
17448 combobox.name = this.name;
17451 if (this.disabled) {
17452 combobox.disabled = true;
17455 var settings = this;
17457 ['xs','sm','md','lg'].map(function(size){
17458 if (settings[size]) {
17459 cfg.cls += ' col-' + size + '-' + settings[size];
17469 initIOSView : function()
17471 this.store.on('load', this.onIOSViewLoad, this);
17476 onIOSViewLoad : function()
17478 if(this.store.getCount() < 1){
17482 this.clearIOSView();
17484 if(this.allowBlank) {
17486 var default_text = '-- SELECT --';
17488 if(this.placeholder.length){
17489 default_text = this.placeholder;
17492 if(this.emptyTitle.length){
17493 default_text += ' - ' + this.emptyTitle + ' -';
17496 var opt = this.inputEl().createChild({
17499 html : default_text
17503 o[this.valueField] = 0;
17504 o[this.displayField] = default_text;
17506 this.ios_options.push({
17513 this.store.data.each(function(d, rowIndex){
17517 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17518 html = d.data[this.displayField];
17523 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17524 value = d.data[this.valueField];
17533 if(this.value == d.data[this.valueField]){
17534 option['selected'] = true;
17537 var opt = this.inputEl().createChild(option);
17539 this.ios_options.push({
17546 this.inputEl().on('change', function(){
17547 this.fireEvent('select', this);
17552 clearIOSView: function()
17554 this.inputEl().dom.innerHTML = '';
17556 this.ios_options = [];
17559 setIOSValue: function(v)
17563 if(!this.ios_options){
17567 Roo.each(this.ios_options, function(opts){
17569 opts.el.dom.removeAttribute('selected');
17571 if(opts.data[this.valueField] != v){
17575 opts.el.dom.setAttribute('selected', true);
17581 * @cfg {Boolean} grow
17585 * @cfg {Number} growMin
17589 * @cfg {Number} growMax
17598 Roo.apply(Roo.bootstrap.ComboBox, {
17602 cls: 'modal-header',
17624 cls: 'list-group-item',
17628 cls: 'roo-combobox-list-group-item-value'
17632 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17646 listItemCheckbox : {
17648 cls: 'list-group-item',
17652 cls: 'roo-combobox-list-group-item-value'
17656 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17672 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17677 cls: 'modal-footer',
17685 cls: 'col-xs-6 text-left',
17688 cls: 'btn btn-danger roo-touch-view-cancel',
17694 cls: 'col-xs-6 text-right',
17697 cls: 'btn btn-success roo-touch-view-ok',
17708 Roo.apply(Roo.bootstrap.ComboBox, {
17710 touchViewTemplate : {
17712 cls: 'modal fade roo-combobox-touch-view',
17716 cls: 'modal-dialog',
17717 style : 'position:fixed', // we have to fix position....
17721 cls: 'modal-content',
17723 Roo.bootstrap.ComboBox.header,
17724 Roo.bootstrap.ComboBox.body,
17725 Roo.bootstrap.ComboBox.footer
17734 * Ext JS Library 1.1.1
17735 * Copyright(c) 2006-2007, Ext JS, LLC.
17737 * Originally Released Under LGPL - original licence link has changed is not relivant.
17740 * <script type="text/javascript">
17745 * @extends Roo.util.Observable
17746 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17747 * This class also supports single and multi selection modes. <br>
17748 * Create a data model bound view:
17750 var store = new Roo.data.Store(...);
17752 var view = new Roo.View({
17754 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17756 singleSelect: true,
17757 selectedClass: "ydataview-selected",
17761 // listen for node click?
17762 view.on("click", function(vw, index, node, e){
17763 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17767 dataModel.load("foobar.xml");
17769 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17771 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17772 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17774 * Note: old style constructor is still suported (container, template, config)
17777 * Create a new View
17778 * @param {Object} config The config object
17781 Roo.View = function(config, depreciated_tpl, depreciated_config){
17783 this.parent = false;
17785 if (typeof(depreciated_tpl) == 'undefined') {
17786 // new way.. - universal constructor.
17787 Roo.apply(this, config);
17788 this.el = Roo.get(this.el);
17791 this.el = Roo.get(config);
17792 this.tpl = depreciated_tpl;
17793 Roo.apply(this, depreciated_config);
17795 this.wrapEl = this.el.wrap().wrap();
17796 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17799 if(typeof(this.tpl) == "string"){
17800 this.tpl = new Roo.Template(this.tpl);
17802 // support xtype ctors..
17803 this.tpl = new Roo.factory(this.tpl, Roo);
17807 this.tpl.compile();
17812 * @event beforeclick
17813 * Fires before a click is processed. Returns false to cancel the default action.
17814 * @param {Roo.View} this
17815 * @param {Number} index The index of the target node
17816 * @param {HTMLElement} node The target node
17817 * @param {Roo.EventObject} e The raw event object
17819 "beforeclick" : true,
17822 * Fires when a template node is clicked.
17823 * @param {Roo.View} this
17824 * @param {Number} index The index of the target node
17825 * @param {HTMLElement} node The target node
17826 * @param {Roo.EventObject} e The raw event object
17831 * Fires when a template node is double clicked.
17832 * @param {Roo.View} this
17833 * @param {Number} index The index of the target node
17834 * @param {HTMLElement} node The target node
17835 * @param {Roo.EventObject} e The raw event object
17839 * @event contextmenu
17840 * Fires when a template node is right clicked.
17841 * @param {Roo.View} this
17842 * @param {Number} index The index of the target node
17843 * @param {HTMLElement} node The target node
17844 * @param {Roo.EventObject} e The raw event object
17846 "contextmenu" : true,
17848 * @event selectionchange
17849 * Fires when the selected nodes change.
17850 * @param {Roo.View} this
17851 * @param {Array} selections Array of the selected nodes
17853 "selectionchange" : true,
17856 * @event beforeselect
17857 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17858 * @param {Roo.View} this
17859 * @param {HTMLElement} node The node to be selected
17860 * @param {Array} selections Array of currently selected nodes
17862 "beforeselect" : true,
17864 * @event preparedata
17865 * Fires on every row to render, to allow you to change the data.
17866 * @param {Roo.View} this
17867 * @param {Object} data to be rendered (change this)
17869 "preparedata" : true
17877 "click": this.onClick,
17878 "dblclick": this.onDblClick,
17879 "contextmenu": this.onContextMenu,
17883 this.selections = [];
17885 this.cmp = new Roo.CompositeElementLite([]);
17887 this.store = Roo.factory(this.store, Roo.data);
17888 this.setStore(this.store, true);
17891 if ( this.footer && this.footer.xtype) {
17893 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17895 this.footer.dataSource = this.store;
17896 this.footer.container = fctr;
17897 this.footer = Roo.factory(this.footer, Roo);
17898 fctr.insertFirst(this.el);
17900 // this is a bit insane - as the paging toolbar seems to detach the el..
17901 // dom.parentNode.parentNode.parentNode
17902 // they get detached?
17906 Roo.View.superclass.constructor.call(this);
17911 Roo.extend(Roo.View, Roo.util.Observable, {
17914 * @cfg {Roo.data.Store} store Data store to load data from.
17919 * @cfg {String|Roo.Element} el The container element.
17924 * @cfg {String|Roo.Template} tpl The template used by this View
17928 * @cfg {String} dataName the named area of the template to use as the data area
17929 * Works with domtemplates roo-name="name"
17933 * @cfg {String} selectedClass The css class to add to selected nodes
17935 selectedClass : "x-view-selected",
17937 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17942 * @cfg {String} text to display on mask (default Loading)
17946 * @cfg {Boolean} multiSelect Allow multiple selection
17948 multiSelect : false,
17950 * @cfg {Boolean} singleSelect Allow single selection
17952 singleSelect: false,
17955 * @cfg {Boolean} toggleSelect - selecting
17957 toggleSelect : false,
17960 * @cfg {Boolean} tickable - selecting
17965 * Returns the element this view is bound to.
17966 * @return {Roo.Element}
17968 getEl : function(){
17969 return this.wrapEl;
17975 * Refreshes the view. - called by datachanged on the store. - do not call directly.
17977 refresh : function(){
17978 //Roo.log('refresh');
17981 // if we are using something like 'domtemplate', then
17982 // the what gets used is:
17983 // t.applySubtemplate(NAME, data, wrapping data..)
17984 // the outer template then get' applied with
17985 // the store 'extra data'
17986 // and the body get's added to the
17987 // roo-name="data" node?
17988 // <span class='roo-tpl-{name}'></span> ?????
17992 this.clearSelections();
17993 this.el.update("");
17995 var records = this.store.getRange();
17996 if(records.length < 1) {
17998 // is this valid?? = should it render a template??
18000 this.el.update(this.emptyText);
18004 if (this.dataName) {
18005 this.el.update(t.apply(this.store.meta)); //????
18006 el = this.el.child('.roo-tpl-' + this.dataName);
18009 for(var i = 0, len = records.length; i < len; i++){
18010 var data = this.prepareData(records[i].data, i, records[i]);
18011 this.fireEvent("preparedata", this, data, i, records[i]);
18013 var d = Roo.apply({}, data);
18016 Roo.apply(d, {'roo-id' : Roo.id()});
18020 Roo.each(this.parent.item, function(item){
18021 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18024 Roo.apply(d, {'roo-data-checked' : 'checked'});
18028 html[html.length] = Roo.util.Format.trim(
18030 t.applySubtemplate(this.dataName, d, this.store.meta) :
18037 el.update(html.join(""));
18038 this.nodes = el.dom.childNodes;
18039 this.updateIndexes(0);
18044 * Function to override to reformat the data that is sent to
18045 * the template for each node.
18046 * DEPRICATED - use the preparedata event handler.
18047 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18048 * a JSON object for an UpdateManager bound view).
18050 prepareData : function(data, index, record)
18052 this.fireEvent("preparedata", this, data, index, record);
18056 onUpdate : function(ds, record){
18057 // Roo.log('on update');
18058 this.clearSelections();
18059 var index = this.store.indexOf(record);
18060 var n = this.nodes[index];
18061 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18062 n.parentNode.removeChild(n);
18063 this.updateIndexes(index, index);
18069 onAdd : function(ds, records, index)
18071 //Roo.log(['on Add', ds, records, index] );
18072 this.clearSelections();
18073 if(this.nodes.length == 0){
18077 var n = this.nodes[index];
18078 for(var i = 0, len = records.length; i < len; i++){
18079 var d = this.prepareData(records[i].data, i, records[i]);
18081 this.tpl.insertBefore(n, d);
18084 this.tpl.append(this.el, d);
18087 this.updateIndexes(index);
18090 onRemove : function(ds, record, index){
18091 // Roo.log('onRemove');
18092 this.clearSelections();
18093 var el = this.dataName ?
18094 this.el.child('.roo-tpl-' + this.dataName) :
18097 el.dom.removeChild(this.nodes[index]);
18098 this.updateIndexes(index);
18102 * Refresh an individual node.
18103 * @param {Number} index
18105 refreshNode : function(index){
18106 this.onUpdate(this.store, this.store.getAt(index));
18109 updateIndexes : function(startIndex, endIndex){
18110 var ns = this.nodes;
18111 startIndex = startIndex || 0;
18112 endIndex = endIndex || ns.length - 1;
18113 for(var i = startIndex; i <= endIndex; i++){
18114 ns[i].nodeIndex = i;
18119 * Changes the data store this view uses and refresh the view.
18120 * @param {Store} store
18122 setStore : function(store, initial){
18123 if(!initial && this.store){
18124 this.store.un("datachanged", this.refresh);
18125 this.store.un("add", this.onAdd);
18126 this.store.un("remove", this.onRemove);
18127 this.store.un("update", this.onUpdate);
18128 this.store.un("clear", this.refresh);
18129 this.store.un("beforeload", this.onBeforeLoad);
18130 this.store.un("load", this.onLoad);
18131 this.store.un("loadexception", this.onLoad);
18135 store.on("datachanged", this.refresh, this);
18136 store.on("add", this.onAdd, this);
18137 store.on("remove", this.onRemove, this);
18138 store.on("update", this.onUpdate, this);
18139 store.on("clear", this.refresh, this);
18140 store.on("beforeload", this.onBeforeLoad, this);
18141 store.on("load", this.onLoad, this);
18142 store.on("loadexception", this.onLoad, this);
18150 * onbeforeLoad - masks the loading area.
18153 onBeforeLoad : function(store,opts)
18155 //Roo.log('onBeforeLoad');
18157 this.el.update("");
18159 this.el.mask(this.mask ? this.mask : "Loading" );
18161 onLoad : function ()
18168 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18169 * @param {HTMLElement} node
18170 * @return {HTMLElement} The template node
18172 findItemFromChild : function(node){
18173 var el = this.dataName ?
18174 this.el.child('.roo-tpl-' + this.dataName,true) :
18177 if(!node || node.parentNode == el){
18180 var p = node.parentNode;
18181 while(p && p != el){
18182 if(p.parentNode == el){
18191 onClick : function(e){
18192 var item = this.findItemFromChild(e.getTarget());
18194 var index = this.indexOf(item);
18195 if(this.onItemClick(item, index, e) !== false){
18196 this.fireEvent("click", this, index, item, e);
18199 this.clearSelections();
18204 onContextMenu : function(e){
18205 var item = this.findItemFromChild(e.getTarget());
18207 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18212 onDblClick : function(e){
18213 var item = this.findItemFromChild(e.getTarget());
18215 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18219 onItemClick : function(item, index, e)
18221 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18224 if (this.toggleSelect) {
18225 var m = this.isSelected(item) ? 'unselect' : 'select';
18228 _t[m](item, true, false);
18231 if(this.multiSelect || this.singleSelect){
18232 if(this.multiSelect && e.shiftKey && this.lastSelection){
18233 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18235 this.select(item, this.multiSelect && e.ctrlKey);
18236 this.lastSelection = item;
18239 if(!this.tickable){
18240 e.preventDefault();
18248 * Get the number of selected nodes.
18251 getSelectionCount : function(){
18252 return this.selections.length;
18256 * Get the currently selected nodes.
18257 * @return {Array} An array of HTMLElements
18259 getSelectedNodes : function(){
18260 return this.selections;
18264 * Get the indexes of the selected nodes.
18267 getSelectedIndexes : function(){
18268 var indexes = [], s = this.selections;
18269 for(var i = 0, len = s.length; i < len; i++){
18270 indexes.push(s[i].nodeIndex);
18276 * Clear all selections
18277 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18279 clearSelections : function(suppressEvent){
18280 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18281 this.cmp.elements = this.selections;
18282 this.cmp.removeClass(this.selectedClass);
18283 this.selections = [];
18284 if(!suppressEvent){
18285 this.fireEvent("selectionchange", this, this.selections);
18291 * Returns true if the passed node is selected
18292 * @param {HTMLElement/Number} node The node or node index
18293 * @return {Boolean}
18295 isSelected : function(node){
18296 var s = this.selections;
18300 node = this.getNode(node);
18301 return s.indexOf(node) !== -1;
18306 * @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
18307 * @param {Boolean} keepExisting (optional) true to keep existing selections
18308 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18310 select : function(nodeInfo, keepExisting, suppressEvent){
18311 if(nodeInfo instanceof Array){
18313 this.clearSelections(true);
18315 for(var i = 0, len = nodeInfo.length; i < len; i++){
18316 this.select(nodeInfo[i], true, true);
18320 var node = this.getNode(nodeInfo);
18321 if(!node || this.isSelected(node)){
18322 return; // already selected.
18325 this.clearSelections(true);
18328 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18329 Roo.fly(node).addClass(this.selectedClass);
18330 this.selections.push(node);
18331 if(!suppressEvent){
18332 this.fireEvent("selectionchange", this, this.selections);
18340 * @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
18341 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18342 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18344 unselect : function(nodeInfo, keepExisting, suppressEvent)
18346 if(nodeInfo instanceof Array){
18347 Roo.each(this.selections, function(s) {
18348 this.unselect(s, nodeInfo);
18352 var node = this.getNode(nodeInfo);
18353 if(!node || !this.isSelected(node)){
18354 //Roo.log("not selected");
18355 return; // not selected.
18359 Roo.each(this.selections, function(s) {
18361 Roo.fly(node).removeClass(this.selectedClass);
18368 this.selections= ns;
18369 this.fireEvent("selectionchange", this, this.selections);
18373 * Gets a template node.
18374 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18375 * @return {HTMLElement} The node or null if it wasn't found
18377 getNode : function(nodeInfo){
18378 if(typeof nodeInfo == "string"){
18379 return document.getElementById(nodeInfo);
18380 }else if(typeof nodeInfo == "number"){
18381 return this.nodes[nodeInfo];
18387 * Gets a range template nodes.
18388 * @param {Number} startIndex
18389 * @param {Number} endIndex
18390 * @return {Array} An array of nodes
18392 getNodes : function(start, end){
18393 var ns = this.nodes;
18394 start = start || 0;
18395 end = typeof end == "undefined" ? ns.length - 1 : end;
18398 for(var i = start; i <= end; i++){
18402 for(var i = start; i >= end; i--){
18410 * Finds the index of the passed node
18411 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18412 * @return {Number} The index of the node or -1
18414 indexOf : function(node){
18415 node = this.getNode(node);
18416 if(typeof node.nodeIndex == "number"){
18417 return node.nodeIndex;
18419 var ns = this.nodes;
18420 for(var i = 0, len = ns.length; i < len; i++){
18431 * based on jquery fullcalendar
18435 Roo.bootstrap = Roo.bootstrap || {};
18437 * @class Roo.bootstrap.Calendar
18438 * @extends Roo.bootstrap.Component
18439 * Bootstrap Calendar class
18440 * @cfg {Boolean} loadMask (true|false) default false
18441 * @cfg {Object} header generate the user specific header of the calendar, default false
18444 * Create a new Container
18445 * @param {Object} config The config object
18450 Roo.bootstrap.Calendar = function(config){
18451 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18455 * Fires when a date is selected
18456 * @param {DatePicker} this
18457 * @param {Date} date The selected date
18461 * @event monthchange
18462 * Fires when the displayed month changes
18463 * @param {DatePicker} this
18464 * @param {Date} date The selected month
18466 'monthchange': true,
18468 * @event evententer
18469 * Fires when mouse over an event
18470 * @param {Calendar} this
18471 * @param {event} Event
18473 'evententer': true,
18475 * @event eventleave
18476 * Fires when the mouse leaves an
18477 * @param {Calendar} this
18480 'eventleave': true,
18482 * @event eventclick
18483 * Fires when the mouse click an
18484 * @param {Calendar} this
18493 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18496 * @cfg {Number} startDay
18497 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18505 getAutoCreate : function(){
18508 var fc_button = function(name, corner, style, content ) {
18509 return Roo.apply({},{
18511 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18513 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18516 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18527 style : 'width:100%',
18534 cls : 'fc-header-left',
18536 fc_button('prev', 'left', 'arrow', '‹' ),
18537 fc_button('next', 'right', 'arrow', '›' ),
18538 { tag: 'span', cls: 'fc-header-space' },
18539 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18547 cls : 'fc-header-center',
18551 cls: 'fc-header-title',
18554 html : 'month / year'
18562 cls : 'fc-header-right',
18564 /* fc_button('month', 'left', '', 'month' ),
18565 fc_button('week', '', '', 'week' ),
18566 fc_button('day', 'right', '', 'day' )
18578 header = this.header;
18581 var cal_heads = function() {
18583 // fixme - handle this.
18585 for (var i =0; i < Date.dayNames.length; i++) {
18586 var d = Date.dayNames[i];
18589 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18590 html : d.substring(0,3)
18594 ret[0].cls += ' fc-first';
18595 ret[6].cls += ' fc-last';
18598 var cal_cell = function(n) {
18601 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18606 cls: 'fc-day-number',
18610 cls: 'fc-day-content',
18614 style: 'position: relative;' // height: 17px;
18626 var cal_rows = function() {
18629 for (var r = 0; r < 6; r++) {
18636 for (var i =0; i < Date.dayNames.length; i++) {
18637 var d = Date.dayNames[i];
18638 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18641 row.cn[0].cls+=' fc-first';
18642 row.cn[0].cn[0].style = 'min-height:90px';
18643 row.cn[6].cls+=' fc-last';
18647 ret[0].cls += ' fc-first';
18648 ret[4].cls += ' fc-prev-last';
18649 ret[5].cls += ' fc-last';
18656 cls: 'fc-border-separate',
18657 style : 'width:100%',
18665 cls : 'fc-first fc-last',
18683 cls : 'fc-content',
18684 style : "position: relative;",
18687 cls : 'fc-view fc-view-month fc-grid',
18688 style : 'position: relative',
18689 unselectable : 'on',
18692 cls : 'fc-event-container',
18693 style : 'position:absolute;z-index:8;top:0;left:0;'
18711 initEvents : function()
18714 throw "can not find store for calendar";
18720 style: "text-align:center",
18724 style: "background-color:white;width:50%;margin:250 auto",
18728 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18739 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18741 var size = this.el.select('.fc-content', true).first().getSize();
18742 this.maskEl.setSize(size.width, size.height);
18743 this.maskEl.enableDisplayMode("block");
18744 if(!this.loadMask){
18745 this.maskEl.hide();
18748 this.store = Roo.factory(this.store, Roo.data);
18749 this.store.on('load', this.onLoad, this);
18750 this.store.on('beforeload', this.onBeforeLoad, this);
18754 this.cells = this.el.select('.fc-day',true);
18755 //Roo.log(this.cells);
18756 this.textNodes = this.el.query('.fc-day-number');
18757 this.cells.addClassOnOver('fc-state-hover');
18759 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18760 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18761 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18762 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18764 this.on('monthchange', this.onMonthChange, this);
18766 this.update(new Date().clearTime());
18769 resize : function() {
18770 var sz = this.el.getSize();
18772 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18773 this.el.select('.fc-day-content div',true).setHeight(34);
18778 showPrevMonth : function(e){
18779 this.update(this.activeDate.add("mo", -1));
18781 showToday : function(e){
18782 this.update(new Date().clearTime());
18785 showNextMonth : function(e){
18786 this.update(this.activeDate.add("mo", 1));
18790 showPrevYear : function(){
18791 this.update(this.activeDate.add("y", -1));
18795 showNextYear : function(){
18796 this.update(this.activeDate.add("y", 1));
18801 update : function(date)
18803 var vd = this.activeDate;
18804 this.activeDate = date;
18805 // if(vd && this.el){
18806 // var t = date.getTime();
18807 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18808 // Roo.log('using add remove');
18810 // this.fireEvent('monthchange', this, date);
18812 // this.cells.removeClass("fc-state-highlight");
18813 // this.cells.each(function(c){
18814 // if(c.dateValue == t){
18815 // c.addClass("fc-state-highlight");
18816 // setTimeout(function(){
18817 // try{c.dom.firstChild.focus();}catch(e){}
18827 var days = date.getDaysInMonth();
18829 var firstOfMonth = date.getFirstDateOfMonth();
18830 var startingPos = firstOfMonth.getDay()-this.startDay;
18832 if(startingPos < this.startDay){
18836 var pm = date.add(Date.MONTH, -1);
18837 var prevStart = pm.getDaysInMonth()-startingPos;
18839 this.cells = this.el.select('.fc-day',true);
18840 this.textNodes = this.el.query('.fc-day-number');
18841 this.cells.addClassOnOver('fc-state-hover');
18843 var cells = this.cells.elements;
18844 var textEls = this.textNodes;
18846 Roo.each(cells, function(cell){
18847 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18850 days += startingPos;
18852 // convert everything to numbers so it's fast
18853 var day = 86400000;
18854 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18857 //Roo.log(prevStart);
18859 var today = new Date().clearTime().getTime();
18860 var sel = date.clearTime().getTime();
18861 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18862 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18863 var ddMatch = this.disabledDatesRE;
18864 var ddText = this.disabledDatesText;
18865 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18866 var ddaysText = this.disabledDaysText;
18867 var format = this.format;
18869 var setCellClass = function(cal, cell){
18873 //Roo.log('set Cell Class');
18875 var t = d.getTime();
18879 cell.dateValue = t;
18881 cell.className += " fc-today";
18882 cell.className += " fc-state-highlight";
18883 cell.title = cal.todayText;
18886 // disable highlight in other month..
18887 //cell.className += " fc-state-highlight";
18892 cell.className = " fc-state-disabled";
18893 cell.title = cal.minText;
18897 cell.className = " fc-state-disabled";
18898 cell.title = cal.maxText;
18902 if(ddays.indexOf(d.getDay()) != -1){
18903 cell.title = ddaysText;
18904 cell.className = " fc-state-disabled";
18907 if(ddMatch && format){
18908 var fvalue = d.dateFormat(format);
18909 if(ddMatch.test(fvalue)){
18910 cell.title = ddText.replace("%0", fvalue);
18911 cell.className = " fc-state-disabled";
18915 if (!cell.initialClassName) {
18916 cell.initialClassName = cell.dom.className;
18919 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18924 for(; i < startingPos; i++) {
18925 textEls[i].innerHTML = (++prevStart);
18926 d.setDate(d.getDate()+1);
18928 cells[i].className = "fc-past fc-other-month";
18929 setCellClass(this, cells[i]);
18934 for(; i < days; i++){
18935 intDay = i - startingPos + 1;
18936 textEls[i].innerHTML = (intDay);
18937 d.setDate(d.getDate()+1);
18939 cells[i].className = ''; // "x-date-active";
18940 setCellClass(this, cells[i]);
18944 for(; i < 42; i++) {
18945 textEls[i].innerHTML = (++extraDays);
18946 d.setDate(d.getDate()+1);
18948 cells[i].className = "fc-future fc-other-month";
18949 setCellClass(this, cells[i]);
18952 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18954 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18956 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18957 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18959 if(totalRows != 6){
18960 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18961 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18964 this.fireEvent('monthchange', this, date);
18968 if(!this.internalRender){
18969 var main = this.el.dom.firstChild;
18970 var w = main.offsetWidth;
18971 this.el.setWidth(w + this.el.getBorderWidth("lr"));
18972 Roo.fly(main).setWidth(w);
18973 this.internalRender = true;
18974 // opera does not respect the auto grow header center column
18975 // then, after it gets a width opera refuses to recalculate
18976 // without a second pass
18977 if(Roo.isOpera && !this.secondPass){
18978 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18979 this.secondPass = true;
18980 this.update.defer(10, this, [date]);
18987 findCell : function(dt) {
18988 dt = dt.clearTime().getTime();
18990 this.cells.each(function(c){
18991 //Roo.log("check " +c.dateValue + '?=' + dt);
18992 if(c.dateValue == dt){
19002 findCells : function(ev) {
19003 var s = ev.start.clone().clearTime().getTime();
19005 var e= ev.end.clone().clearTime().getTime();
19008 this.cells.each(function(c){
19009 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19011 if(c.dateValue > e){
19014 if(c.dateValue < s){
19023 // findBestRow: function(cells)
19027 // for (var i =0 ; i < cells.length;i++) {
19028 // ret = Math.max(cells[i].rows || 0,ret);
19035 addItem : function(ev)
19037 // look for vertical location slot in
19038 var cells = this.findCells(ev);
19040 // ev.row = this.findBestRow(cells);
19042 // work out the location.
19046 for(var i =0; i < cells.length; i++) {
19048 cells[i].row = cells[0].row;
19051 cells[i].row = cells[i].row + 1;
19061 if (crow.start.getY() == cells[i].getY()) {
19063 crow.end = cells[i];
19080 cells[0].events.push(ev);
19082 this.calevents.push(ev);
19085 clearEvents: function() {
19087 if(!this.calevents){
19091 Roo.each(this.cells.elements, function(c){
19097 Roo.each(this.calevents, function(e) {
19098 Roo.each(e.els, function(el) {
19099 el.un('mouseenter' ,this.onEventEnter, this);
19100 el.un('mouseleave' ,this.onEventLeave, this);
19105 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19111 renderEvents: function()
19115 this.cells.each(function(c) {
19124 if(c.row != c.events.length){
19125 r = 4 - (4 - (c.row - c.events.length));
19128 c.events = ev.slice(0, r);
19129 c.more = ev.slice(r);
19131 if(c.more.length && c.more.length == 1){
19132 c.events.push(c.more.pop());
19135 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19139 this.cells.each(function(c) {
19141 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19144 for (var e = 0; e < c.events.length; e++){
19145 var ev = c.events[e];
19146 var rows = ev.rows;
19148 for(var i = 0; i < rows.length; i++) {
19150 // how many rows should it span..
19153 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19154 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19156 unselectable : "on",
19159 cls: 'fc-event-inner',
19163 // cls: 'fc-event-time',
19164 // html : cells.length > 1 ? '' : ev.time
19168 cls: 'fc-event-title',
19169 html : String.format('{0}', ev.title)
19176 cls: 'ui-resizable-handle ui-resizable-e',
19177 html : '  '
19184 cfg.cls += ' fc-event-start';
19186 if ((i+1) == rows.length) {
19187 cfg.cls += ' fc-event-end';
19190 var ctr = _this.el.select('.fc-event-container',true).first();
19191 var cg = ctr.createChild(cfg);
19193 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19194 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19196 var r = (c.more.length) ? 1 : 0;
19197 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19198 cg.setWidth(ebox.right - sbox.x -2);
19200 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19201 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19202 cg.on('click', _this.onEventClick, _this, ev);
19213 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19214 style : 'position: absolute',
19215 unselectable : "on",
19218 cls: 'fc-event-inner',
19222 cls: 'fc-event-title',
19230 cls: 'ui-resizable-handle ui-resizable-e',
19231 html : '  '
19237 var ctr = _this.el.select('.fc-event-container',true).first();
19238 var cg = ctr.createChild(cfg);
19240 var sbox = c.select('.fc-day-content',true).first().getBox();
19241 var ebox = c.select('.fc-day-content',true).first().getBox();
19243 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19244 cg.setWidth(ebox.right - sbox.x -2);
19246 cg.on('click', _this.onMoreEventClick, _this, c.more);
19256 onEventEnter: function (e, el,event,d) {
19257 this.fireEvent('evententer', this, el, event);
19260 onEventLeave: function (e, el,event,d) {
19261 this.fireEvent('eventleave', this, el, event);
19264 onEventClick: function (e, el,event,d) {
19265 this.fireEvent('eventclick', this, el, event);
19268 onMonthChange: function () {
19272 onMoreEventClick: function(e, el, more)
19276 this.calpopover.placement = 'right';
19277 this.calpopover.setTitle('More');
19279 this.calpopover.setContent('');
19281 var ctr = this.calpopover.el.select('.popover-content', true).first();
19283 Roo.each(more, function(m){
19285 cls : 'fc-event-hori fc-event-draggable',
19288 var cg = ctr.createChild(cfg);
19290 cg.on('click', _this.onEventClick, _this, m);
19293 this.calpopover.show(el);
19298 onLoad: function ()
19300 this.calevents = [];
19303 if(this.store.getCount() > 0){
19304 this.store.data.each(function(d){
19307 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19308 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19309 time : d.data.start_time,
19310 title : d.data.title,
19311 description : d.data.description,
19312 venue : d.data.venue
19317 this.renderEvents();
19319 if(this.calevents.length && this.loadMask){
19320 this.maskEl.hide();
19324 onBeforeLoad: function()
19326 this.clearEvents();
19328 this.maskEl.show();
19342 * @class Roo.bootstrap.Popover
19343 * @extends Roo.bootstrap.Component
19344 * Bootstrap Popover class
19345 * @cfg {String} html contents of the popover (or false to use children..)
19346 * @cfg {String} title of popover (or false to hide)
19347 * @cfg {String} placement how it is placed
19348 * @cfg {String} trigger click || hover (or false to trigger manually)
19349 * @cfg {String} over what (parent or false to trigger manually.)
19350 * @cfg {Number} delay - delay before showing
19353 * Create a new Popover
19354 * @param {Object} config The config object
19357 Roo.bootstrap.Popover = function(config){
19358 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19364 * After the popover show
19366 * @param {Roo.bootstrap.Popover} this
19371 * After the popover hide
19373 * @param {Roo.bootstrap.Popover} this
19379 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19381 title: 'Fill in a title',
19384 placement : 'right',
19385 trigger : 'hover', // hover
19391 can_build_overlaid : false,
19393 getChildContainer : function()
19395 return this.el.select('.popover-content',true).first();
19398 getAutoCreate : function(){
19401 cls : 'popover roo-dynamic',
19402 style: 'display:block',
19408 cls : 'popover-inner',
19412 cls: 'popover-title popover-header',
19416 cls : 'popover-content popover-body',
19427 setTitle: function(str)
19430 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19432 setContent: function(str)
19435 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19437 // as it get's added to the bottom of the page.
19438 onRender : function(ct, position)
19440 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19442 var cfg = Roo.apply({}, this.getAutoCreate());
19446 cfg.cls += ' ' + this.cls;
19449 cfg.style = this.style;
19451 //Roo.log("adding to ");
19452 this.el = Roo.get(document.body).createChild(cfg, position);
19453 // Roo.log(this.el);
19458 initEvents : function()
19460 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19461 this.el.enableDisplayMode('block');
19463 if (this.over === false) {
19466 if (this.triggers === false) {
19469 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19470 var triggers = this.trigger ? this.trigger.split(' ') : [];
19471 Roo.each(triggers, function(trigger) {
19473 if (trigger == 'click') {
19474 on_el.on('click', this.toggle, this);
19475 } else if (trigger != 'manual') {
19476 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19477 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19479 on_el.on(eventIn ,this.enter, this);
19480 on_el.on(eventOut, this.leave, this);
19491 toggle : function () {
19492 this.hoverState == 'in' ? this.leave() : this.enter();
19495 enter : function () {
19497 clearTimeout(this.timeout);
19499 this.hoverState = 'in';
19501 if (!this.delay || !this.delay.show) {
19506 this.timeout = setTimeout(function () {
19507 if (_t.hoverState == 'in') {
19510 }, this.delay.show)
19513 leave : function() {
19514 clearTimeout(this.timeout);
19516 this.hoverState = 'out';
19518 if (!this.delay || !this.delay.hide) {
19523 this.timeout = setTimeout(function () {
19524 if (_t.hoverState == 'out') {
19527 }, this.delay.hide)
19530 show : function (on_el)
19533 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19537 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19538 if (this.html !== false) {
19539 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19541 this.el.removeClass([
19542 'fade','top','bottom', 'left', 'right','in',
19543 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19545 if (!this.title.length) {
19546 this.el.select('.popover-title',true).hide();
19549 var placement = typeof this.placement == 'function' ?
19550 this.placement.call(this, this.el, on_el) :
19553 var autoToken = /\s?auto?\s?/i;
19554 var autoPlace = autoToken.test(placement);
19556 placement = placement.replace(autoToken, '') || 'top';
19560 //this.el.setXY([0,0]);
19562 this.el.dom.style.display='block';
19563 this.el.addClass(placement);
19565 //this.el.appendTo(on_el);
19567 var p = this.getPosition();
19568 var box = this.el.getBox();
19573 var align = Roo.bootstrap.Popover.alignment[placement];
19576 this.el.alignTo(on_el, align[0],align[1]);
19577 //var arrow = this.el.select('.arrow',true).first();
19578 //arrow.set(align[2],
19580 this.el.addClass('in');
19583 if (this.el.hasClass('fade')) {
19587 this.hoverState = 'in';
19589 this.fireEvent('show', this);
19594 this.el.setXY([0,0]);
19595 this.el.removeClass('in');
19597 this.hoverState = null;
19599 this.fireEvent('hide', this);
19604 Roo.bootstrap.Popover.alignment = {
19605 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19606 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19607 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19608 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19619 * @class Roo.bootstrap.Progress
19620 * @extends Roo.bootstrap.Component
19621 * Bootstrap Progress class
19622 * @cfg {Boolean} striped striped of the progress bar
19623 * @cfg {Boolean} active animated of the progress bar
19627 * Create a new Progress
19628 * @param {Object} config The config object
19631 Roo.bootstrap.Progress = function(config){
19632 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19635 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19640 getAutoCreate : function(){
19648 cfg.cls += ' progress-striped';
19652 cfg.cls += ' active';
19671 * @class Roo.bootstrap.ProgressBar
19672 * @extends Roo.bootstrap.Component
19673 * Bootstrap ProgressBar class
19674 * @cfg {Number} aria_valuenow aria-value now
19675 * @cfg {Number} aria_valuemin aria-value min
19676 * @cfg {Number} aria_valuemax aria-value max
19677 * @cfg {String} label label for the progress bar
19678 * @cfg {String} panel (success | info | warning | danger )
19679 * @cfg {String} role role of the progress bar
19680 * @cfg {String} sr_only text
19684 * Create a new ProgressBar
19685 * @param {Object} config The config object
19688 Roo.bootstrap.ProgressBar = function(config){
19689 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19692 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19696 aria_valuemax : 100,
19702 getAutoCreate : function()
19707 cls: 'progress-bar',
19708 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19720 cfg.role = this.role;
19723 if(this.aria_valuenow){
19724 cfg['aria-valuenow'] = this.aria_valuenow;
19727 if(this.aria_valuemin){
19728 cfg['aria-valuemin'] = this.aria_valuemin;
19731 if(this.aria_valuemax){
19732 cfg['aria-valuemax'] = this.aria_valuemax;
19735 if(this.label && !this.sr_only){
19736 cfg.html = this.label;
19740 cfg.cls += ' progress-bar-' + this.panel;
19746 update : function(aria_valuenow)
19748 this.aria_valuenow = aria_valuenow;
19750 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19765 * @class Roo.bootstrap.TabGroup
19766 * @extends Roo.bootstrap.Column
19767 * Bootstrap Column class
19768 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19769 * @cfg {Boolean} carousel true to make the group behave like a carousel
19770 * @cfg {Boolean} bullets show bullets for the panels
19771 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19772 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19773 * @cfg {Boolean} showarrow (true|false) show arrow default true
19776 * Create a new TabGroup
19777 * @param {Object} config The config object
19780 Roo.bootstrap.TabGroup = function(config){
19781 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19783 this.navId = Roo.id();
19786 Roo.bootstrap.TabGroup.register(this);
19790 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19793 transition : false,
19798 slideOnTouch : false,
19801 getAutoCreate : function()
19803 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19805 cfg.cls += ' tab-content';
19807 if (this.carousel) {
19808 cfg.cls += ' carousel slide';
19811 cls : 'carousel-inner',
19815 if(this.bullets && !Roo.isTouch){
19818 cls : 'carousel-bullets',
19822 if(this.bullets_cls){
19823 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19830 cfg.cn[0].cn.push(bullets);
19833 if(this.showarrow){
19834 cfg.cn[0].cn.push({
19836 class : 'carousel-arrow',
19840 class : 'carousel-prev',
19844 class : 'fa fa-chevron-left'
19850 class : 'carousel-next',
19854 class : 'fa fa-chevron-right'
19867 initEvents: function()
19869 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19870 // this.el.on("touchstart", this.onTouchStart, this);
19873 if(this.autoslide){
19876 this.slideFn = window.setInterval(function() {
19877 _this.showPanelNext();
19881 if(this.showarrow){
19882 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19883 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19889 // onTouchStart : function(e, el, o)
19891 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19895 // this.showPanelNext();
19899 getChildContainer : function()
19901 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19905 * register a Navigation item
19906 * @param {Roo.bootstrap.NavItem} the navitem to add
19908 register : function(item)
19910 this.tabs.push( item);
19911 item.navId = this.navId; // not really needed..
19916 getActivePanel : function()
19919 Roo.each(this.tabs, function(t) {
19929 getPanelByName : function(n)
19932 Roo.each(this.tabs, function(t) {
19933 if (t.tabId == n) {
19941 indexOfPanel : function(p)
19944 Roo.each(this.tabs, function(t,i) {
19945 if (t.tabId == p.tabId) {
19954 * show a specific panel
19955 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19956 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19958 showPanel : function (pan)
19960 if(this.transition || typeof(pan) == 'undefined'){
19961 Roo.log("waiting for the transitionend");
19965 if (typeof(pan) == 'number') {
19966 pan = this.tabs[pan];
19969 if (typeof(pan) == 'string') {
19970 pan = this.getPanelByName(pan);
19973 var cur = this.getActivePanel();
19976 Roo.log('pan or acitve pan is undefined');
19980 if (pan.tabId == this.getActivePanel().tabId) {
19984 if (false === cur.fireEvent('beforedeactivate')) {
19988 if(this.bullets > 0 && !Roo.isTouch){
19989 this.setActiveBullet(this.indexOfPanel(pan));
19992 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19994 //class="carousel-item carousel-item-next carousel-item-left"
19996 this.transition = true;
19997 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
19998 var lr = dir == 'next' ? 'left' : 'right';
19999 pan.el.addClass(dir); // or prev
20000 pan.el.addClass('carousel-item-' + dir); // or prev
20001 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20002 cur.el.addClass(lr); // or right
20003 pan.el.addClass(lr);
20004 cur.el.addClass('carousel-item-' +lr); // or right
20005 pan.el.addClass('carousel-item-' +lr);
20009 cur.el.on('transitionend', function() {
20010 Roo.log("trans end?");
20012 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20013 pan.setActive(true);
20015 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20016 cur.setActive(false);
20018 _this.transition = false;
20020 }, this, { single: true } );
20025 cur.setActive(false);
20026 pan.setActive(true);
20031 showPanelNext : function()
20033 var i = this.indexOfPanel(this.getActivePanel());
20035 if (i >= this.tabs.length - 1 && !this.autoslide) {
20039 if (i >= this.tabs.length - 1 && this.autoslide) {
20043 this.showPanel(this.tabs[i+1]);
20046 showPanelPrev : function()
20048 var i = this.indexOfPanel(this.getActivePanel());
20050 if (i < 1 && !this.autoslide) {
20054 if (i < 1 && this.autoslide) {
20055 i = this.tabs.length;
20058 this.showPanel(this.tabs[i-1]);
20062 addBullet: function()
20064 if(!this.bullets || Roo.isTouch){
20067 var ctr = this.el.select('.carousel-bullets',true).first();
20068 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20069 var bullet = ctr.createChild({
20070 cls : 'bullet bullet-' + i
20071 },ctr.dom.lastChild);
20076 bullet.on('click', (function(e, el, o, ii, t){
20078 e.preventDefault();
20080 this.showPanel(ii);
20082 if(this.autoslide && this.slideFn){
20083 clearInterval(this.slideFn);
20084 this.slideFn = window.setInterval(function() {
20085 _this.showPanelNext();
20089 }).createDelegate(this, [i, bullet], true));
20094 setActiveBullet : function(i)
20100 Roo.each(this.el.select('.bullet', true).elements, function(el){
20101 el.removeClass('selected');
20104 var bullet = this.el.select('.bullet-' + i, true).first();
20110 bullet.addClass('selected');
20121 Roo.apply(Roo.bootstrap.TabGroup, {
20125 * register a Navigation Group
20126 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20128 register : function(navgrp)
20130 this.groups[navgrp.navId] = navgrp;
20134 * fetch a Navigation Group based on the navigation ID
20135 * if one does not exist , it will get created.
20136 * @param {string} the navgroup to add
20137 * @returns {Roo.bootstrap.NavGroup} the navgroup
20139 get: function(navId) {
20140 if (typeof(this.groups[navId]) == 'undefined') {
20141 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20143 return this.groups[navId] ;
20158 * @class Roo.bootstrap.TabPanel
20159 * @extends Roo.bootstrap.Component
20160 * Bootstrap TabPanel class
20161 * @cfg {Boolean} active panel active
20162 * @cfg {String} html panel content
20163 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20164 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20165 * @cfg {String} href click to link..
20169 * Create a new TabPanel
20170 * @param {Object} config The config object
20173 Roo.bootstrap.TabPanel = function(config){
20174 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20178 * Fires when the active status changes
20179 * @param {Roo.bootstrap.TabPanel} this
20180 * @param {Boolean} state the new state
20185 * @event beforedeactivate
20186 * Fires before a tab is de-activated - can be used to do validation on a form.
20187 * @param {Roo.bootstrap.TabPanel} this
20188 * @return {Boolean} false if there is an error
20191 'beforedeactivate': true
20194 this.tabId = this.tabId || Roo.id();
20198 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20206 getAutoCreate : function(){
20211 // item is needed for carousel - not sure if it has any effect otherwise
20212 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20213 html: this.html || ''
20217 cfg.cls += ' active';
20221 cfg.tabId = this.tabId;
20229 initEvents: function()
20231 var p = this.parent();
20233 this.navId = this.navId || p.navId;
20235 if (typeof(this.navId) != 'undefined') {
20236 // not really needed.. but just in case.. parent should be a NavGroup.
20237 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20241 var i = tg.tabs.length - 1;
20243 if(this.active && tg.bullets > 0 && i < tg.bullets){
20244 tg.setActiveBullet(i);
20248 this.el.on('click', this.onClick, this);
20251 this.el.on("touchstart", this.onTouchStart, this);
20252 this.el.on("touchmove", this.onTouchMove, this);
20253 this.el.on("touchend", this.onTouchEnd, this);
20258 onRender : function(ct, position)
20260 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20263 setActive : function(state)
20265 Roo.log("panel - set active " + this.tabId + "=" + state);
20267 this.active = state;
20269 this.el.removeClass('active');
20271 } else if (!this.el.hasClass('active')) {
20272 this.el.addClass('active');
20275 this.fireEvent('changed', this, state);
20278 onClick : function(e)
20280 e.preventDefault();
20282 if(!this.href.length){
20286 window.location.href = this.href;
20295 onTouchStart : function(e)
20297 this.swiping = false;
20299 this.startX = e.browserEvent.touches[0].clientX;
20300 this.startY = e.browserEvent.touches[0].clientY;
20303 onTouchMove : function(e)
20305 this.swiping = true;
20307 this.endX = e.browserEvent.touches[0].clientX;
20308 this.endY = e.browserEvent.touches[0].clientY;
20311 onTouchEnd : function(e)
20318 var tabGroup = this.parent();
20320 if(this.endX > this.startX){ // swiping right
20321 tabGroup.showPanelPrev();
20325 if(this.startX > this.endX){ // swiping left
20326 tabGroup.showPanelNext();
20345 * @class Roo.bootstrap.DateField
20346 * @extends Roo.bootstrap.Input
20347 * Bootstrap DateField class
20348 * @cfg {Number} weekStart default 0
20349 * @cfg {String} viewMode default empty, (months|years)
20350 * @cfg {String} minViewMode default empty, (months|years)
20351 * @cfg {Number} startDate default -Infinity
20352 * @cfg {Number} endDate default Infinity
20353 * @cfg {Boolean} todayHighlight default false
20354 * @cfg {Boolean} todayBtn default false
20355 * @cfg {Boolean} calendarWeeks default false
20356 * @cfg {Object} daysOfWeekDisabled default empty
20357 * @cfg {Boolean} singleMode default false (true | false)
20359 * @cfg {Boolean} keyboardNavigation default true
20360 * @cfg {String} language default en
20363 * Create a new DateField
20364 * @param {Object} config The config object
20367 Roo.bootstrap.DateField = function(config){
20368 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20372 * Fires when this field show.
20373 * @param {Roo.bootstrap.DateField} this
20374 * @param {Mixed} date The date value
20379 * Fires when this field hide.
20380 * @param {Roo.bootstrap.DateField} this
20381 * @param {Mixed} date The date value
20386 * Fires when select a date.
20387 * @param {Roo.bootstrap.DateField} this
20388 * @param {Mixed} date The date value
20392 * @event beforeselect
20393 * Fires when before select a date.
20394 * @param {Roo.bootstrap.DateField} this
20395 * @param {Mixed} date The date value
20397 beforeselect : true
20401 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20404 * @cfg {String} format
20405 * The default date format string which can be overriden for localization support. The format must be
20406 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20410 * @cfg {String} altFormats
20411 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20412 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20414 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20422 todayHighlight : false,
20428 keyboardNavigation: true,
20430 calendarWeeks: false,
20432 startDate: -Infinity,
20436 daysOfWeekDisabled: [],
20440 singleMode : false,
20442 UTCDate: function()
20444 return new Date(Date.UTC.apply(Date, arguments));
20447 UTCToday: function()
20449 var today = new Date();
20450 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20453 getDate: function() {
20454 var d = this.getUTCDate();
20455 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20458 getUTCDate: function() {
20462 setDate: function(d) {
20463 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20466 setUTCDate: function(d) {
20468 this.setValue(this.formatDate(this.date));
20471 onRender: function(ct, position)
20474 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20476 this.language = this.language || 'en';
20477 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20478 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20480 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20481 this.format = this.format || 'm/d/y';
20482 this.isInline = false;
20483 this.isInput = true;
20484 this.component = this.el.select('.add-on', true).first() || false;
20485 this.component = (this.component && this.component.length === 0) ? false : this.component;
20486 this.hasInput = this.component && this.inputEl().length;
20488 if (typeof(this.minViewMode === 'string')) {
20489 switch (this.minViewMode) {
20491 this.minViewMode = 1;
20494 this.minViewMode = 2;
20497 this.minViewMode = 0;
20502 if (typeof(this.viewMode === 'string')) {
20503 switch (this.viewMode) {
20516 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20518 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20520 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20522 this.picker().on('mousedown', this.onMousedown, this);
20523 this.picker().on('click', this.onClick, this);
20525 this.picker().addClass('datepicker-dropdown');
20527 this.startViewMode = this.viewMode;
20529 if(this.singleMode){
20530 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20531 v.setVisibilityMode(Roo.Element.DISPLAY);
20535 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20536 v.setStyle('width', '189px');
20540 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20541 if(!this.calendarWeeks){
20546 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20547 v.attr('colspan', function(i, val){
20548 return parseInt(val) + 1;
20553 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20555 this.setStartDate(this.startDate);
20556 this.setEndDate(this.endDate);
20558 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20565 if(this.isInline) {
20570 picker : function()
20572 return this.pickerEl;
20573 // return this.el.select('.datepicker', true).first();
20576 fillDow: function()
20578 var dowCnt = this.weekStart;
20587 if(this.calendarWeeks){
20595 while (dowCnt < this.weekStart + 7) {
20599 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20603 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20606 fillMonths: function()
20609 var months = this.picker().select('>.datepicker-months td', true).first();
20611 months.dom.innerHTML = '';
20617 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20620 months.createChild(month);
20627 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;
20629 if (this.date < this.startDate) {
20630 this.viewDate = new Date(this.startDate);
20631 } else if (this.date > this.endDate) {
20632 this.viewDate = new Date(this.endDate);
20634 this.viewDate = new Date(this.date);
20642 var d = new Date(this.viewDate),
20643 year = d.getUTCFullYear(),
20644 month = d.getUTCMonth(),
20645 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20646 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20647 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20648 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20649 currentDate = this.date && this.date.valueOf(),
20650 today = this.UTCToday();
20652 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20654 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20656 // this.picker.select('>tfoot th.today').
20657 // .text(dates[this.language].today)
20658 // .toggle(this.todayBtn !== false);
20660 this.updateNavArrows();
20663 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20665 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20667 prevMonth.setUTCDate(day);
20669 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20671 var nextMonth = new Date(prevMonth);
20673 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20675 nextMonth = nextMonth.valueOf();
20677 var fillMonths = false;
20679 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20681 while(prevMonth.valueOf() <= nextMonth) {
20684 if (prevMonth.getUTCDay() === this.weekStart) {
20686 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20694 if(this.calendarWeeks){
20695 // ISO 8601: First week contains first thursday.
20696 // ISO also states week starts on Monday, but we can be more abstract here.
20698 // Start of current week: based on weekstart/current date
20699 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20700 // Thursday of this week
20701 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20702 // First Thursday of year, year from thursday
20703 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20704 // Calendar week: ms between thursdays, div ms per day, div 7 days
20705 calWeek = (th - yth) / 864e5 / 7 + 1;
20707 fillMonths.cn.push({
20715 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20717 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20720 if (this.todayHighlight &&
20721 prevMonth.getUTCFullYear() == today.getFullYear() &&
20722 prevMonth.getUTCMonth() == today.getMonth() &&
20723 prevMonth.getUTCDate() == today.getDate()) {
20724 clsName += ' today';
20727 if (currentDate && prevMonth.valueOf() === currentDate) {
20728 clsName += ' active';
20731 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20732 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20733 clsName += ' disabled';
20736 fillMonths.cn.push({
20738 cls: 'day ' + clsName,
20739 html: prevMonth.getDate()
20742 prevMonth.setDate(prevMonth.getDate()+1);
20745 var currentYear = this.date && this.date.getUTCFullYear();
20746 var currentMonth = this.date && this.date.getUTCMonth();
20748 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20750 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20751 v.removeClass('active');
20753 if(currentYear === year && k === currentMonth){
20754 v.addClass('active');
20757 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20758 v.addClass('disabled');
20764 year = parseInt(year/10, 10) * 10;
20766 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20768 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20771 for (var i = -1; i < 11; i++) {
20772 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20774 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20782 showMode: function(dir)
20785 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20788 Roo.each(this.picker().select('>div',true).elements, function(v){
20789 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20792 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20797 if(this.isInline) {
20801 this.picker().removeClass(['bottom', 'top']);
20803 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20805 * place to the top of element!
20809 this.picker().addClass('top');
20810 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20815 this.picker().addClass('bottom');
20817 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20820 parseDate : function(value)
20822 if(!value || value instanceof Date){
20825 var v = Date.parseDate(value, this.format);
20826 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20827 v = Date.parseDate(value, 'Y-m-d');
20829 if(!v && this.altFormats){
20830 if(!this.altFormatsArray){
20831 this.altFormatsArray = this.altFormats.split("|");
20833 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20834 v = Date.parseDate(value, this.altFormatsArray[i]);
20840 formatDate : function(date, fmt)
20842 return (!date || !(date instanceof Date)) ?
20843 date : date.dateFormat(fmt || this.format);
20846 onFocus : function()
20848 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20852 onBlur : function()
20854 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20856 var d = this.inputEl().getValue();
20863 showPopup : function()
20865 this.picker().show();
20869 this.fireEvent('showpopup', this, this.date);
20872 hidePopup : function()
20874 if(this.isInline) {
20877 this.picker().hide();
20878 this.viewMode = this.startViewMode;
20881 this.fireEvent('hidepopup', this, this.date);
20885 onMousedown: function(e)
20887 e.stopPropagation();
20888 e.preventDefault();
20893 Roo.bootstrap.DateField.superclass.keyup.call(this);
20897 setValue: function(v)
20899 if(this.fireEvent('beforeselect', this, v) !== false){
20900 var d = new Date(this.parseDate(v) ).clearTime();
20902 if(isNaN(d.getTime())){
20903 this.date = this.viewDate = '';
20904 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20908 v = this.formatDate(d);
20910 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20912 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20916 this.fireEvent('select', this, this.date);
20920 getValue: function()
20922 return this.formatDate(this.date);
20925 fireKey: function(e)
20927 if (!this.picker().isVisible()){
20928 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20934 var dateChanged = false,
20936 newDate, newViewDate;
20941 e.preventDefault();
20945 if (!this.keyboardNavigation) {
20948 dir = e.keyCode == 37 ? -1 : 1;
20951 newDate = this.moveYear(this.date, dir);
20952 newViewDate = this.moveYear(this.viewDate, dir);
20953 } else if (e.shiftKey){
20954 newDate = this.moveMonth(this.date, dir);
20955 newViewDate = this.moveMonth(this.viewDate, dir);
20957 newDate = new Date(this.date);
20958 newDate.setUTCDate(this.date.getUTCDate() + dir);
20959 newViewDate = new Date(this.viewDate);
20960 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20962 if (this.dateWithinRange(newDate)){
20963 this.date = newDate;
20964 this.viewDate = newViewDate;
20965 this.setValue(this.formatDate(this.date));
20967 e.preventDefault();
20968 dateChanged = true;
20973 if (!this.keyboardNavigation) {
20976 dir = e.keyCode == 38 ? -1 : 1;
20978 newDate = this.moveYear(this.date, dir);
20979 newViewDate = this.moveYear(this.viewDate, dir);
20980 } else if (e.shiftKey){
20981 newDate = this.moveMonth(this.date, dir);
20982 newViewDate = this.moveMonth(this.viewDate, dir);
20984 newDate = new Date(this.date);
20985 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20986 newViewDate = new Date(this.viewDate);
20987 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20989 if (this.dateWithinRange(newDate)){
20990 this.date = newDate;
20991 this.viewDate = newViewDate;
20992 this.setValue(this.formatDate(this.date));
20994 e.preventDefault();
20995 dateChanged = true;
20999 this.setValue(this.formatDate(this.date));
21001 e.preventDefault();
21004 this.setValue(this.formatDate(this.date));
21018 onClick: function(e)
21020 e.stopPropagation();
21021 e.preventDefault();
21023 var target = e.getTarget();
21025 if(target.nodeName.toLowerCase() === 'i'){
21026 target = Roo.get(target).dom.parentNode;
21029 var nodeName = target.nodeName;
21030 var className = target.className;
21031 var html = target.innerHTML;
21032 //Roo.log(nodeName);
21034 switch(nodeName.toLowerCase()) {
21036 switch(className) {
21042 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21043 switch(this.viewMode){
21045 this.viewDate = this.moveMonth(this.viewDate, dir);
21049 this.viewDate = this.moveYear(this.viewDate, dir);
21055 var date = new Date();
21056 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21058 this.setValue(this.formatDate(this.date));
21065 if (className.indexOf('disabled') < 0) {
21066 this.viewDate.setUTCDate(1);
21067 if (className.indexOf('month') > -1) {
21068 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21070 var year = parseInt(html, 10) || 0;
21071 this.viewDate.setUTCFullYear(year);
21075 if(this.singleMode){
21076 this.setValue(this.formatDate(this.viewDate));
21087 //Roo.log(className);
21088 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21089 var day = parseInt(html, 10) || 1;
21090 var year = this.viewDate.getUTCFullYear(),
21091 month = this.viewDate.getUTCMonth();
21093 if (className.indexOf('old') > -1) {
21100 } else if (className.indexOf('new') > -1) {
21108 //Roo.log([year,month,day]);
21109 this.date = this.UTCDate(year, month, day,0,0,0,0);
21110 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21112 //Roo.log(this.formatDate(this.date));
21113 this.setValue(this.formatDate(this.date));
21120 setStartDate: function(startDate)
21122 this.startDate = startDate || -Infinity;
21123 if (this.startDate !== -Infinity) {
21124 this.startDate = this.parseDate(this.startDate);
21127 this.updateNavArrows();
21130 setEndDate: function(endDate)
21132 this.endDate = endDate || Infinity;
21133 if (this.endDate !== Infinity) {
21134 this.endDate = this.parseDate(this.endDate);
21137 this.updateNavArrows();
21140 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21142 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21143 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21144 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21146 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21147 return parseInt(d, 10);
21150 this.updateNavArrows();
21153 updateNavArrows: function()
21155 if(this.singleMode){
21159 var d = new Date(this.viewDate),
21160 year = d.getUTCFullYear(),
21161 month = d.getUTCMonth();
21163 Roo.each(this.picker().select('.prev', true).elements, function(v){
21165 switch (this.viewMode) {
21168 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21174 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21181 Roo.each(this.picker().select('.next', true).elements, function(v){
21183 switch (this.viewMode) {
21186 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21192 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21200 moveMonth: function(date, dir)
21205 var new_date = new Date(date.valueOf()),
21206 day = new_date.getUTCDate(),
21207 month = new_date.getUTCMonth(),
21208 mag = Math.abs(dir),
21210 dir = dir > 0 ? 1 : -1;
21213 // If going back one month, make sure month is not current month
21214 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21216 return new_date.getUTCMonth() == month;
21218 // If going forward one month, make sure month is as expected
21219 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21221 return new_date.getUTCMonth() != new_month;
21223 new_month = month + dir;
21224 new_date.setUTCMonth(new_month);
21225 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21226 if (new_month < 0 || new_month > 11) {
21227 new_month = (new_month + 12) % 12;
21230 // For magnitudes >1, move one month at a time...
21231 for (var i=0; i<mag; i++) {
21232 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21233 new_date = this.moveMonth(new_date, dir);
21235 // ...then reset the day, keeping it in the new month
21236 new_month = new_date.getUTCMonth();
21237 new_date.setUTCDate(day);
21239 return new_month != new_date.getUTCMonth();
21242 // Common date-resetting loop -- if date is beyond end of month, make it
21245 new_date.setUTCDate(--day);
21246 new_date.setUTCMonth(new_month);
21251 moveYear: function(date, dir)
21253 return this.moveMonth(date, dir*12);
21256 dateWithinRange: function(date)
21258 return date >= this.startDate && date <= this.endDate;
21264 this.picker().remove();
21267 validateValue : function(value)
21269 if(this.getVisibilityEl().hasClass('hidden')){
21273 if(value.length < 1) {
21274 if(this.allowBlank){
21280 if(value.length < this.minLength){
21283 if(value.length > this.maxLength){
21287 var vt = Roo.form.VTypes;
21288 if(!vt[this.vtype](value, this)){
21292 if(typeof this.validator == "function"){
21293 var msg = this.validator(value);
21299 if(this.regex && !this.regex.test(value)){
21303 if(typeof(this.parseDate(value)) == 'undefined'){
21307 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21311 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21321 this.date = this.viewDate = '';
21323 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21328 Roo.apply(Roo.bootstrap.DateField, {
21339 html: '<i class="fa fa-arrow-left"/>'
21349 html: '<i class="fa fa-arrow-right"/>'
21391 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21392 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21393 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21394 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21395 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21408 navFnc: 'FullYear',
21413 navFnc: 'FullYear',
21418 Roo.apply(Roo.bootstrap.DateField, {
21422 cls: 'datepicker dropdown-menu roo-dynamic',
21426 cls: 'datepicker-days',
21430 cls: 'table-condensed',
21432 Roo.bootstrap.DateField.head,
21436 Roo.bootstrap.DateField.footer
21443 cls: 'datepicker-months',
21447 cls: 'table-condensed',
21449 Roo.bootstrap.DateField.head,
21450 Roo.bootstrap.DateField.content,
21451 Roo.bootstrap.DateField.footer
21458 cls: 'datepicker-years',
21462 cls: 'table-condensed',
21464 Roo.bootstrap.DateField.head,
21465 Roo.bootstrap.DateField.content,
21466 Roo.bootstrap.DateField.footer
21485 * @class Roo.bootstrap.TimeField
21486 * @extends Roo.bootstrap.Input
21487 * Bootstrap DateField class
21491 * Create a new TimeField
21492 * @param {Object} config The config object
21495 Roo.bootstrap.TimeField = function(config){
21496 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21500 * Fires when this field show.
21501 * @param {Roo.bootstrap.DateField} thisthis
21502 * @param {Mixed} date The date value
21507 * Fires when this field hide.
21508 * @param {Roo.bootstrap.DateField} this
21509 * @param {Mixed} date The date value
21514 * Fires when select a date.
21515 * @param {Roo.bootstrap.DateField} this
21516 * @param {Mixed} date The date value
21522 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21525 * @cfg {String} format
21526 * The default time format string which can be overriden for localization support. The format must be
21527 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21531 onRender: function(ct, position)
21534 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21536 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21538 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21540 this.pop = this.picker().select('>.datepicker-time',true).first();
21541 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21543 this.picker().on('mousedown', this.onMousedown, this);
21544 this.picker().on('click', this.onClick, this);
21546 this.picker().addClass('datepicker-dropdown');
21551 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21552 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21553 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21554 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21555 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21556 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21560 fireKey: function(e){
21561 if (!this.picker().isVisible()){
21562 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21568 e.preventDefault();
21576 this.onTogglePeriod();
21579 this.onIncrementMinutes();
21582 this.onDecrementMinutes();
21591 onClick: function(e) {
21592 e.stopPropagation();
21593 e.preventDefault();
21596 picker : function()
21598 return this.el.select('.datepicker', true).first();
21601 fillTime: function()
21603 var time = this.pop.select('tbody', true).first();
21605 time.dom.innerHTML = '';
21620 cls: 'hours-up glyphicon glyphicon-chevron-up'
21640 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21661 cls: 'timepicker-hour',
21676 cls: 'timepicker-minute',
21691 cls: 'btn btn-primary period',
21713 cls: 'hours-down glyphicon glyphicon-chevron-down'
21733 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21751 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21758 var hours = this.time.getHours();
21759 var minutes = this.time.getMinutes();
21772 hours = hours - 12;
21776 hours = '0' + hours;
21780 minutes = '0' + minutes;
21783 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21784 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21785 this.pop.select('button', true).first().dom.innerHTML = period;
21791 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21793 var cls = ['bottom'];
21795 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21802 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21807 this.picker().addClass(cls.join('-'));
21811 Roo.each(cls, function(c){
21813 _this.picker().setTop(_this.inputEl().getHeight());
21817 _this.picker().setTop(0 - _this.picker().getHeight());
21822 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21826 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21833 onFocus : function()
21835 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21839 onBlur : function()
21841 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21847 this.picker().show();
21852 this.fireEvent('show', this, this.date);
21857 this.picker().hide();
21860 this.fireEvent('hide', this, this.date);
21863 setTime : function()
21866 this.setValue(this.time.format(this.format));
21868 this.fireEvent('select', this, this.date);
21873 onMousedown: function(e){
21874 e.stopPropagation();
21875 e.preventDefault();
21878 onIncrementHours: function()
21880 Roo.log('onIncrementHours');
21881 this.time = this.time.add(Date.HOUR, 1);
21886 onDecrementHours: function()
21888 Roo.log('onDecrementHours');
21889 this.time = this.time.add(Date.HOUR, -1);
21893 onIncrementMinutes: function()
21895 Roo.log('onIncrementMinutes');
21896 this.time = this.time.add(Date.MINUTE, 1);
21900 onDecrementMinutes: function()
21902 Roo.log('onDecrementMinutes');
21903 this.time = this.time.add(Date.MINUTE, -1);
21907 onTogglePeriod: function()
21909 Roo.log('onTogglePeriod');
21910 this.time = this.time.add(Date.HOUR, 12);
21917 Roo.apply(Roo.bootstrap.TimeField, {
21947 cls: 'btn btn-info ok',
21959 Roo.apply(Roo.bootstrap.TimeField, {
21963 cls: 'datepicker dropdown-menu',
21967 cls: 'datepicker-time',
21971 cls: 'table-condensed',
21973 Roo.bootstrap.TimeField.content,
21974 Roo.bootstrap.TimeField.footer
21993 * @class Roo.bootstrap.MonthField
21994 * @extends Roo.bootstrap.Input
21995 * Bootstrap MonthField class
21997 * @cfg {String} language default en
22000 * Create a new MonthField
22001 * @param {Object} config The config object
22004 Roo.bootstrap.MonthField = function(config){
22005 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22010 * Fires when this field show.
22011 * @param {Roo.bootstrap.MonthField} this
22012 * @param {Mixed} date The date value
22017 * Fires when this field hide.
22018 * @param {Roo.bootstrap.MonthField} this
22019 * @param {Mixed} date The date value
22024 * Fires when select a date.
22025 * @param {Roo.bootstrap.MonthField} this
22026 * @param {String} oldvalue The old value
22027 * @param {String} newvalue The new value
22033 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22035 onRender: function(ct, position)
22038 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22040 this.language = this.language || 'en';
22041 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22042 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22044 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22045 this.isInline = false;
22046 this.isInput = true;
22047 this.component = this.el.select('.add-on', true).first() || false;
22048 this.component = (this.component && this.component.length === 0) ? false : this.component;
22049 this.hasInput = this.component && this.inputEL().length;
22051 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22053 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22055 this.picker().on('mousedown', this.onMousedown, this);
22056 this.picker().on('click', this.onClick, this);
22058 this.picker().addClass('datepicker-dropdown');
22060 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22061 v.setStyle('width', '189px');
22068 if(this.isInline) {
22074 setValue: function(v, suppressEvent)
22076 var o = this.getValue();
22078 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22082 if(suppressEvent !== true){
22083 this.fireEvent('select', this, o, v);
22088 getValue: function()
22093 onClick: function(e)
22095 e.stopPropagation();
22096 e.preventDefault();
22098 var target = e.getTarget();
22100 if(target.nodeName.toLowerCase() === 'i'){
22101 target = Roo.get(target).dom.parentNode;
22104 var nodeName = target.nodeName;
22105 var className = target.className;
22106 var html = target.innerHTML;
22108 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22112 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22114 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22120 picker : function()
22122 return this.pickerEl;
22125 fillMonths: function()
22128 var months = this.picker().select('>.datepicker-months td', true).first();
22130 months.dom.innerHTML = '';
22136 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22139 months.createChild(month);
22148 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22149 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22152 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22153 e.removeClass('active');
22155 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22156 e.addClass('active');
22163 if(this.isInline) {
22167 this.picker().removeClass(['bottom', 'top']);
22169 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22171 * place to the top of element!
22175 this.picker().addClass('top');
22176 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22181 this.picker().addClass('bottom');
22183 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22186 onFocus : function()
22188 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22192 onBlur : function()
22194 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22196 var d = this.inputEl().getValue();
22205 this.picker().show();
22206 this.picker().select('>.datepicker-months', true).first().show();
22210 this.fireEvent('show', this, this.date);
22215 if(this.isInline) {
22218 this.picker().hide();
22219 this.fireEvent('hide', this, this.date);
22223 onMousedown: function(e)
22225 e.stopPropagation();
22226 e.preventDefault();
22231 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22235 fireKey: function(e)
22237 if (!this.picker().isVisible()){
22238 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22249 e.preventDefault();
22253 dir = e.keyCode == 37 ? -1 : 1;
22255 this.vIndex = this.vIndex + dir;
22257 if(this.vIndex < 0){
22261 if(this.vIndex > 11){
22265 if(isNaN(this.vIndex)){
22269 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22275 dir = e.keyCode == 38 ? -1 : 1;
22277 this.vIndex = this.vIndex + dir * 4;
22279 if(this.vIndex < 0){
22283 if(this.vIndex > 11){
22287 if(isNaN(this.vIndex)){
22291 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22296 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22297 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22301 e.preventDefault();
22304 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22305 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22321 this.picker().remove();
22326 Roo.apply(Roo.bootstrap.MonthField, {
22345 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22346 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22351 Roo.apply(Roo.bootstrap.MonthField, {
22355 cls: 'datepicker dropdown-menu roo-dynamic',
22359 cls: 'datepicker-months',
22363 cls: 'table-condensed',
22365 Roo.bootstrap.DateField.content
22385 * @class Roo.bootstrap.CheckBox
22386 * @extends Roo.bootstrap.Input
22387 * Bootstrap CheckBox class
22389 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22390 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22391 * @cfg {String} boxLabel The text that appears beside the checkbox
22392 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22393 * @cfg {Boolean} checked initnal the element
22394 * @cfg {Boolean} inline inline the element (default false)
22395 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22396 * @cfg {String} tooltip label tooltip
22399 * Create a new CheckBox
22400 * @param {Object} config The config object
22403 Roo.bootstrap.CheckBox = function(config){
22404 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22409 * Fires when the element is checked or unchecked.
22410 * @param {Roo.bootstrap.CheckBox} this This input
22411 * @param {Boolean} checked The new checked value
22416 * Fires when the element is click.
22417 * @param {Roo.bootstrap.CheckBox} this This input
22424 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22426 inputType: 'checkbox',
22435 // checkbox success does not make any sense really..
22440 getAutoCreate : function()
22442 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22448 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22451 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22457 type : this.inputType,
22458 value : this.inputValue,
22459 cls : 'roo-' + this.inputType, //'form-box',
22460 placeholder : this.placeholder || ''
22464 if(this.inputType != 'radio'){
22468 cls : 'roo-hidden-value',
22469 value : this.checked ? this.inputValue : this.valueOff
22474 if (this.weight) { // Validity check?
22475 cfg.cls += " " + this.inputType + "-" + this.weight;
22478 if (this.disabled) {
22479 input.disabled=true;
22483 input.checked = this.checked;
22488 input.name = this.name;
22490 if(this.inputType != 'radio'){
22491 hidden.name = this.name;
22492 input.name = '_hidden_' + this.name;
22497 input.cls += ' input-' + this.size;
22502 ['xs','sm','md','lg'].map(function(size){
22503 if (settings[size]) {
22504 cfg.cls += ' col-' + size + '-' + settings[size];
22508 var inputblock = input;
22510 if (this.before || this.after) {
22513 cls : 'input-group',
22518 inputblock.cn.push({
22520 cls : 'input-group-addon',
22525 inputblock.cn.push(input);
22527 if(this.inputType != 'radio'){
22528 inputblock.cn.push(hidden);
22532 inputblock.cn.push({
22534 cls : 'input-group-addon',
22540 var boxLabelCfg = false;
22546 //'for': id, // box label is handled by onclick - so no for...
22548 html: this.boxLabel
22551 boxLabelCfg.tooltip = this.tooltip;
22557 if (align ==='left' && this.fieldLabel.length) {
22558 // Roo.log("left and has label");
22563 cls : 'control-label',
22564 html : this.fieldLabel
22575 cfg.cn[1].cn.push(boxLabelCfg);
22578 if(this.labelWidth > 12){
22579 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22582 if(this.labelWidth < 13 && this.labelmd == 0){
22583 this.labelmd = this.labelWidth;
22586 if(this.labellg > 0){
22587 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22588 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22591 if(this.labelmd > 0){
22592 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22593 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22596 if(this.labelsm > 0){
22597 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22598 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22601 if(this.labelxs > 0){
22602 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22603 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22606 } else if ( this.fieldLabel.length) {
22607 // Roo.log(" label");
22611 tag: this.boxLabel ? 'span' : 'label',
22613 cls: 'control-label box-input-label',
22614 //cls : 'input-group-addon',
22615 html : this.fieldLabel
22622 cfg.cn.push(boxLabelCfg);
22627 // Roo.log(" no label && no align");
22628 cfg.cn = [ inputblock ] ;
22630 cfg.cn.push(boxLabelCfg);
22638 if(this.inputType != 'radio'){
22639 cfg.cn.push(hidden);
22647 * return the real input element.
22649 inputEl: function ()
22651 return this.el.select('input.roo-' + this.inputType,true).first();
22653 hiddenEl: function ()
22655 return this.el.select('input.roo-hidden-value',true).first();
22658 labelEl: function()
22660 return this.el.select('label.control-label',true).first();
22662 /* depricated... */
22666 return this.labelEl();
22669 boxLabelEl: function()
22671 return this.el.select('label.box-label',true).first();
22674 initEvents : function()
22676 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22678 this.inputEl().on('click', this.onClick, this);
22680 if (this.boxLabel) {
22681 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22684 this.startValue = this.getValue();
22687 Roo.bootstrap.CheckBox.register(this);
22691 onClick : function(e)
22693 if(this.fireEvent('click', this, e) !== false){
22694 this.setChecked(!this.checked);
22699 setChecked : function(state,suppressEvent)
22701 this.startValue = this.getValue();
22703 if(this.inputType == 'radio'){
22705 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22706 e.dom.checked = false;
22709 this.inputEl().dom.checked = true;
22711 this.inputEl().dom.value = this.inputValue;
22713 if(suppressEvent !== true){
22714 this.fireEvent('check', this, true);
22722 this.checked = state;
22724 this.inputEl().dom.checked = state;
22727 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22729 if(suppressEvent !== true){
22730 this.fireEvent('check', this, state);
22736 getValue : function()
22738 if(this.inputType == 'radio'){
22739 return this.getGroupValue();
22742 return this.hiddenEl().dom.value;
22746 getGroupValue : function()
22748 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22752 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22755 setValue : function(v,suppressEvent)
22757 if(this.inputType == 'radio'){
22758 this.setGroupValue(v, suppressEvent);
22762 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22767 setGroupValue : function(v, suppressEvent)
22769 this.startValue = this.getValue();
22771 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22772 e.dom.checked = false;
22774 if(e.dom.value == v){
22775 e.dom.checked = true;
22779 if(suppressEvent !== true){
22780 this.fireEvent('check', this, true);
22788 validate : function()
22790 if(this.getVisibilityEl().hasClass('hidden')){
22796 (this.inputType == 'radio' && this.validateRadio()) ||
22797 (this.inputType == 'checkbox' && this.validateCheckbox())
22803 this.markInvalid();
22807 validateRadio : function()
22809 if(this.getVisibilityEl().hasClass('hidden')){
22813 if(this.allowBlank){
22819 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22820 if(!e.dom.checked){
22832 validateCheckbox : function()
22835 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22836 //return (this.getValue() == this.inputValue) ? true : false;
22839 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22847 for(var i in group){
22848 if(group[i].el.isVisible(true)){
22856 for(var i in group){
22861 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22868 * Mark this field as valid
22870 markValid : function()
22874 this.fireEvent('valid', this);
22876 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22879 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22886 if(this.inputType == 'radio'){
22887 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22888 var fg = e.findParent('.form-group', false, true);
22889 if (Roo.bootstrap.version == 3) {
22890 fg.removeClass([_this.invalidClass, _this.validClass]);
22891 fg.addClass(_this.validClass);
22893 fg.removeClass(['is-valid', 'is-invalid']);
22894 fg.addClass('is-valid');
22902 var fg = this.el.findParent('.form-group', false, true);
22903 if (Roo.bootstrap.version == 3) {
22904 fg.removeClass([this.invalidClass, this.validClass]);
22905 fg.addClass(this.validClass);
22907 fg.removeClass(['is-valid', 'is-invalid']);
22908 fg.addClass('is-valid');
22913 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22919 for(var i in group){
22920 var fg = group[i].el.findParent('.form-group', false, true);
22921 if (Roo.bootstrap.version == 3) {
22922 fg.removeClass([this.invalidClass, this.validClass]);
22923 fg.addClass(this.validClass);
22925 fg.removeClass(['is-valid', 'is-invalid']);
22926 fg.addClass('is-valid');
22932 * Mark this field as invalid
22933 * @param {String} msg The validation message
22935 markInvalid : function(msg)
22937 if(this.allowBlank){
22943 this.fireEvent('invalid', this, msg);
22945 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22948 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22952 label.markInvalid();
22955 if(this.inputType == 'radio'){
22957 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22958 var fg = e.findParent('.form-group', false, true);
22959 if (Roo.bootstrap.version == 3) {
22960 fg.removeClass([_this.invalidClass, _this.validClass]);
22961 fg.addClass(_this.invalidClass);
22963 fg.removeClass(['is-invalid', 'is-valid']);
22964 fg.addClass('is-invalid');
22972 var fg = this.el.findParent('.form-group', false, true);
22973 if (Roo.bootstrap.version == 3) {
22974 fg.removeClass([_this.invalidClass, _this.validClass]);
22975 fg.addClass(_this.invalidClass);
22977 fg.removeClass(['is-invalid', 'is-valid']);
22978 fg.addClass('is-invalid');
22983 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22989 for(var i in group){
22990 var fg = group[i].el.findParent('.form-group', false, true);
22991 if (Roo.bootstrap.version == 3) {
22992 fg.removeClass([_this.invalidClass, _this.validClass]);
22993 fg.addClass(_this.invalidClass);
22995 fg.removeClass(['is-invalid', 'is-valid']);
22996 fg.addClass('is-invalid');
23002 clearInvalid : function()
23004 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23006 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23008 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23010 if (label && label.iconEl) {
23011 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23012 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23016 disable : function()
23018 if(this.inputType != 'radio'){
23019 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23026 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23027 _this.getActionEl().addClass(this.disabledClass);
23028 e.dom.disabled = true;
23032 this.disabled = true;
23033 this.fireEvent("disable", this);
23037 enable : function()
23039 if(this.inputType != 'radio'){
23040 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23047 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23048 _this.getActionEl().removeClass(this.disabledClass);
23049 e.dom.disabled = false;
23053 this.disabled = false;
23054 this.fireEvent("enable", this);
23058 setBoxLabel : function(v)
23063 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23069 Roo.apply(Roo.bootstrap.CheckBox, {
23074 * register a CheckBox Group
23075 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23077 register : function(checkbox)
23079 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23080 this.groups[checkbox.groupId] = {};
23083 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23087 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23091 * fetch a CheckBox Group based on the group ID
23092 * @param {string} the group ID
23093 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23095 get: function(groupId) {
23096 if (typeof(this.groups[groupId]) == 'undefined') {
23100 return this.groups[groupId] ;
23113 * @class Roo.bootstrap.Radio
23114 * @extends Roo.bootstrap.Component
23115 * Bootstrap Radio class
23116 * @cfg {String} boxLabel - the label associated
23117 * @cfg {String} value - the value of radio
23120 * Create a new Radio
23121 * @param {Object} config The config object
23123 Roo.bootstrap.Radio = function(config){
23124 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23128 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23134 getAutoCreate : function()
23138 cls : 'form-group radio',
23143 html : this.boxLabel
23151 initEvents : function()
23153 this.parent().register(this);
23155 this.el.on('click', this.onClick, this);
23159 onClick : function(e)
23161 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23162 this.setChecked(true);
23166 setChecked : function(state, suppressEvent)
23168 this.parent().setValue(this.value, suppressEvent);
23172 setBoxLabel : function(v)
23177 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23192 * @class Roo.bootstrap.SecurePass
23193 * @extends Roo.bootstrap.Input
23194 * Bootstrap SecurePass class
23198 * Create a new SecurePass
23199 * @param {Object} config The config object
23202 Roo.bootstrap.SecurePass = function (config) {
23203 // these go here, so the translation tool can replace them..
23205 PwdEmpty: "Please type a password, and then retype it to confirm.",
23206 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23207 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23208 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23209 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23210 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23211 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23212 TooWeak: "Your password is Too Weak."
23214 this.meterLabel = "Password strength:";
23215 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23216 this.meterClass = [
23217 "roo-password-meter-tooweak",
23218 "roo-password-meter-weak",
23219 "roo-password-meter-medium",
23220 "roo-password-meter-strong",
23221 "roo-password-meter-grey"
23226 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23229 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23231 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23233 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23234 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23235 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23236 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23237 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23238 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23239 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23249 * @cfg {String/Object} Label for the strength meter (defaults to
23250 * 'Password strength:')
23255 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23256 * ['Weak', 'Medium', 'Strong'])
23259 pwdStrengths: false,
23272 initEvents: function ()
23274 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23276 if (this.el.is('input[type=password]') && Roo.isSafari) {
23277 this.el.on('keydown', this.SafariOnKeyDown, this);
23280 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23283 onRender: function (ct, position)
23285 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23286 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23287 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23289 this.trigger.createChild({
23294 cls: 'roo-password-meter-grey col-xs-12',
23297 //width: this.meterWidth + 'px'
23301 cls: 'roo-password-meter-text'
23307 if (this.hideTrigger) {
23308 this.trigger.setDisplayed(false);
23310 this.setSize(this.width || '', this.height || '');
23313 onDestroy: function ()
23315 if (this.trigger) {
23316 this.trigger.removeAllListeners();
23317 this.trigger.remove();
23320 this.wrap.remove();
23322 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23325 checkStrength: function ()
23327 var pwd = this.inputEl().getValue();
23328 if (pwd == this._lastPwd) {
23333 if (this.ClientSideStrongPassword(pwd)) {
23335 } else if (this.ClientSideMediumPassword(pwd)) {
23337 } else if (this.ClientSideWeakPassword(pwd)) {
23343 Roo.log('strength1: ' + strength);
23345 //var pm = this.trigger.child('div/div/div').dom;
23346 var pm = this.trigger.child('div/div');
23347 pm.removeClass(this.meterClass);
23348 pm.addClass(this.meterClass[strength]);
23351 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23353 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23355 this._lastPwd = pwd;
23359 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23361 this._lastPwd = '';
23363 var pm = this.trigger.child('div/div');
23364 pm.removeClass(this.meterClass);
23365 pm.addClass('roo-password-meter-grey');
23368 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23371 this.inputEl().dom.type='password';
23374 validateValue: function (value)
23377 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23380 if (value.length == 0) {
23381 if (this.allowBlank) {
23382 this.clearInvalid();
23386 this.markInvalid(this.errors.PwdEmpty);
23387 this.errorMsg = this.errors.PwdEmpty;
23395 if ('[\x21-\x7e]*'.match(value)) {
23396 this.markInvalid(this.errors.PwdBadChar);
23397 this.errorMsg = this.errors.PwdBadChar;
23400 if (value.length < 6) {
23401 this.markInvalid(this.errors.PwdShort);
23402 this.errorMsg = this.errors.PwdShort;
23405 if (value.length > 16) {
23406 this.markInvalid(this.errors.PwdLong);
23407 this.errorMsg = this.errors.PwdLong;
23411 if (this.ClientSideStrongPassword(value)) {
23413 } else if (this.ClientSideMediumPassword(value)) {
23415 } else if (this.ClientSideWeakPassword(value)) {
23422 if (strength < 2) {
23423 //this.markInvalid(this.errors.TooWeak);
23424 this.errorMsg = this.errors.TooWeak;
23429 console.log('strength2: ' + strength);
23431 //var pm = this.trigger.child('div/div/div').dom;
23433 var pm = this.trigger.child('div/div');
23434 pm.removeClass(this.meterClass);
23435 pm.addClass(this.meterClass[strength]);
23437 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23439 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23441 this.errorMsg = '';
23445 CharacterSetChecks: function (type)
23448 this.fResult = false;
23451 isctype: function (character, type)
23454 case this.kCapitalLetter:
23455 if (character >= 'A' && character <= 'Z') {
23460 case this.kSmallLetter:
23461 if (character >= 'a' && character <= 'z') {
23467 if (character >= '0' && character <= '9') {
23472 case this.kPunctuation:
23473 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23484 IsLongEnough: function (pwd, size)
23486 return !(pwd == null || isNaN(size) || pwd.length < size);
23489 SpansEnoughCharacterSets: function (word, nb)
23491 if (!this.IsLongEnough(word, nb))
23496 var characterSetChecks = new Array(
23497 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23498 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23501 for (var index = 0; index < word.length; ++index) {
23502 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23503 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23504 characterSetChecks[nCharSet].fResult = true;
23511 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23512 if (characterSetChecks[nCharSet].fResult) {
23517 if (nCharSets < nb) {
23523 ClientSideStrongPassword: function (pwd)
23525 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23528 ClientSideMediumPassword: function (pwd)
23530 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23533 ClientSideWeakPassword: function (pwd)
23535 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23538 })//<script type="text/javascript">
23541 * Based Ext JS Library 1.1.1
23542 * Copyright(c) 2006-2007, Ext JS, LLC.
23548 * @class Roo.HtmlEditorCore
23549 * @extends Roo.Component
23550 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23552 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23555 Roo.HtmlEditorCore = function(config){
23558 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23563 * @event initialize
23564 * Fires when the editor is fully initialized (including the iframe)
23565 * @param {Roo.HtmlEditorCore} this
23570 * Fires when the editor is first receives the focus. Any insertion must wait
23571 * until after this event.
23572 * @param {Roo.HtmlEditorCore} this
23576 * @event beforesync
23577 * Fires before the textarea is updated with content from the editor iframe. Return false
23578 * to cancel the sync.
23579 * @param {Roo.HtmlEditorCore} this
23580 * @param {String} html
23584 * @event beforepush
23585 * Fires before the iframe editor is updated with content from the textarea. Return false
23586 * to cancel the push.
23587 * @param {Roo.HtmlEditorCore} this
23588 * @param {String} html
23593 * Fires when the textarea is updated with content from the editor iframe.
23594 * @param {Roo.HtmlEditorCore} this
23595 * @param {String} html
23600 * Fires when the iframe editor is updated with content from the textarea.
23601 * @param {Roo.HtmlEditorCore} this
23602 * @param {String} html
23607 * @event editorevent
23608 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23609 * @param {Roo.HtmlEditorCore} this
23615 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23617 // defaults : white / black...
23618 this.applyBlacklists();
23625 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23629 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23635 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23640 * @cfg {Number} height (in pixels)
23644 * @cfg {Number} width (in pixels)
23649 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23652 stylesheets: false,
23657 // private properties
23658 validationEvent : false,
23660 initialized : false,
23662 sourceEditMode : false,
23663 onFocus : Roo.emptyFn,
23665 hideMode:'offsets',
23669 // blacklist + whitelisted elements..
23676 * Protected method that will not generally be called directly. It
23677 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23678 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23680 getDocMarkup : function(){
23684 // inherit styels from page...??
23685 if (this.stylesheets === false) {
23687 Roo.get(document.head).select('style').each(function(node) {
23688 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23691 Roo.get(document.head).select('link').each(function(node) {
23692 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23695 } else if (!this.stylesheets.length) {
23697 st = '<style type="text/css">' +
23698 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23701 st = '<style type="text/css">' +
23706 st += '<style type="text/css">' +
23707 'IMG { cursor: pointer } ' +
23710 var cls = 'roo-htmleditor-body';
23712 if(this.bodyCls.length){
23713 cls += ' ' + this.bodyCls;
23716 return '<html><head>' + st +
23717 //<style type="text/css">' +
23718 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23720 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23724 onRender : function(ct, position)
23727 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23728 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23731 this.el.dom.style.border = '0 none';
23732 this.el.dom.setAttribute('tabIndex', -1);
23733 this.el.addClass('x-hidden hide');
23737 if(Roo.isIE){ // fix IE 1px bogus margin
23738 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23742 this.frameId = Roo.id();
23746 var iframe = this.owner.wrap.createChild({
23748 cls: 'form-control', // bootstrap..
23750 name: this.frameId,
23751 frameBorder : 'no',
23752 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23757 this.iframe = iframe.dom;
23759 this.assignDocWin();
23761 this.doc.designMode = 'on';
23764 this.doc.write(this.getDocMarkup());
23768 var task = { // must defer to wait for browser to be ready
23770 //console.log("run task?" + this.doc.readyState);
23771 this.assignDocWin();
23772 if(this.doc.body || this.doc.readyState == 'complete'){
23774 this.doc.designMode="on";
23778 Roo.TaskMgr.stop(task);
23779 this.initEditor.defer(10, this);
23786 Roo.TaskMgr.start(task);
23791 onResize : function(w, h)
23793 Roo.log('resize: ' +w + ',' + h );
23794 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23798 if(typeof w == 'number'){
23800 this.iframe.style.width = w + 'px';
23802 if(typeof h == 'number'){
23804 this.iframe.style.height = h + 'px';
23806 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23813 * Toggles the editor between standard and source edit mode.
23814 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23816 toggleSourceEdit : function(sourceEditMode){
23818 this.sourceEditMode = sourceEditMode === true;
23820 if(this.sourceEditMode){
23822 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23825 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23826 //this.iframe.className = '';
23829 //this.setSize(this.owner.wrap.getSize());
23830 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23837 * Protected method that will not generally be called directly. If you need/want
23838 * custom HTML cleanup, this is the method you should override.
23839 * @param {String} html The HTML to be cleaned
23840 * return {String} The cleaned HTML
23842 cleanHtml : function(html){
23843 html = String(html);
23844 if(html.length > 5){
23845 if(Roo.isSafari){ // strip safari nonsense
23846 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23849 if(html == ' '){
23856 * HTML Editor -> Textarea
23857 * Protected method that will not generally be called directly. Syncs the contents
23858 * of the editor iframe with the textarea.
23860 syncValue : function(){
23861 if(this.initialized){
23862 var bd = (this.doc.body || this.doc.documentElement);
23863 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23864 var html = bd.innerHTML;
23866 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23867 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23869 html = '<div style="'+m[0]+'">' + html + '</div>';
23872 html = this.cleanHtml(html);
23873 // fix up the special chars.. normaly like back quotes in word...
23874 // however we do not want to do this with chinese..
23875 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23877 var cc = match.charCodeAt();
23879 // Get the character value, handling surrogate pairs
23880 if (match.length == 2) {
23881 // It's a surrogate pair, calculate the Unicode code point
23882 var high = match.charCodeAt(0) - 0xD800;
23883 var low = match.charCodeAt(1) - 0xDC00;
23884 cc = (high * 0x400) + low + 0x10000;
23886 (cc >= 0x4E00 && cc < 0xA000 ) ||
23887 (cc >= 0x3400 && cc < 0x4E00 ) ||
23888 (cc >= 0xf900 && cc < 0xfb00 )
23893 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23894 return "&#" + cc + ";";
23901 if(this.owner.fireEvent('beforesync', this, html) !== false){
23902 this.el.dom.value = html;
23903 this.owner.fireEvent('sync', this, html);
23909 * Protected method that will not generally be called directly. Pushes the value of the textarea
23910 * into the iframe editor.
23912 pushValue : function(){
23913 if(this.initialized){
23914 var v = this.el.dom.value.trim();
23916 // if(v.length < 1){
23920 if(this.owner.fireEvent('beforepush', this, v) !== false){
23921 var d = (this.doc.body || this.doc.documentElement);
23923 this.cleanUpPaste();
23924 this.el.dom.value = d.innerHTML;
23925 this.owner.fireEvent('push', this, v);
23931 deferFocus : function(){
23932 this.focus.defer(10, this);
23936 focus : function(){
23937 if(this.win && !this.sourceEditMode){
23944 assignDocWin: function()
23946 var iframe = this.iframe;
23949 this.doc = iframe.contentWindow.document;
23950 this.win = iframe.contentWindow;
23952 // if (!Roo.get(this.frameId)) {
23955 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23956 // this.win = Roo.get(this.frameId).dom.contentWindow;
23958 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23962 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23963 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23968 initEditor : function(){
23969 //console.log("INIT EDITOR");
23970 this.assignDocWin();
23974 this.doc.designMode="on";
23976 this.doc.write(this.getDocMarkup());
23979 var dbody = (this.doc.body || this.doc.documentElement);
23980 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23981 // this copies styles from the containing element into thsi one..
23982 // not sure why we need all of this..
23983 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23985 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23986 //ss['background-attachment'] = 'fixed'; // w3c
23987 dbody.bgProperties = 'fixed'; // ie
23988 //Roo.DomHelper.applyStyles(dbody, ss);
23989 Roo.EventManager.on(this.doc, {
23990 //'mousedown': this.onEditorEvent,
23991 'mouseup': this.onEditorEvent,
23992 'dblclick': this.onEditorEvent,
23993 'click': this.onEditorEvent,
23994 'keyup': this.onEditorEvent,
23999 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24001 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24002 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24004 this.initialized = true;
24006 this.owner.fireEvent('initialize', this);
24011 onDestroy : function(){
24017 //for (var i =0; i < this.toolbars.length;i++) {
24018 // // fixme - ask toolbars for heights?
24019 // this.toolbars[i].onDestroy();
24022 //this.wrap.dom.innerHTML = '';
24023 //this.wrap.remove();
24028 onFirstFocus : function(){
24030 this.assignDocWin();
24033 this.activated = true;
24036 if(Roo.isGecko){ // prevent silly gecko errors
24038 var s = this.win.getSelection();
24039 if(!s.focusNode || s.focusNode.nodeType != 3){
24040 var r = s.getRangeAt(0);
24041 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24046 this.execCmd('useCSS', true);
24047 this.execCmd('styleWithCSS', false);
24050 this.owner.fireEvent('activate', this);
24054 adjustFont: function(btn){
24055 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24056 //if(Roo.isSafari){ // safari
24059 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24060 if(Roo.isSafari){ // safari
24061 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24062 v = (v < 10) ? 10 : v;
24063 v = (v > 48) ? 48 : v;
24064 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24069 v = Math.max(1, v+adjust);
24071 this.execCmd('FontSize', v );
24074 onEditorEvent : function(e)
24076 this.owner.fireEvent('editorevent', this, e);
24077 // this.updateToolbar();
24078 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24081 insertTag : function(tg)
24083 // could be a bit smarter... -> wrap the current selected tRoo..
24084 if (tg.toLowerCase() == 'span' ||
24085 tg.toLowerCase() == 'code' ||
24086 tg.toLowerCase() == 'sup' ||
24087 tg.toLowerCase() == 'sub'
24090 range = this.createRange(this.getSelection());
24091 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24092 wrappingNode.appendChild(range.extractContents());
24093 range.insertNode(wrappingNode);
24100 this.execCmd("formatblock", tg);
24104 insertText : function(txt)
24108 var range = this.createRange();
24109 range.deleteContents();
24110 //alert(Sender.getAttribute('label'));
24112 range.insertNode(this.doc.createTextNode(txt));
24118 * Executes a Midas editor command on the editor document and performs necessary focus and
24119 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24120 * @param {String} cmd The Midas command
24121 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24123 relayCmd : function(cmd, value){
24125 this.execCmd(cmd, value);
24126 this.owner.fireEvent('editorevent', this);
24127 //this.updateToolbar();
24128 this.owner.deferFocus();
24132 * Executes a Midas editor command directly on the editor document.
24133 * For visual commands, you should use {@link #relayCmd} instead.
24134 * <b>This should only be called after the editor is initialized.</b>
24135 * @param {String} cmd The Midas command
24136 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24138 execCmd : function(cmd, value){
24139 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24146 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24148 * @param {String} text | dom node..
24150 insertAtCursor : function(text)
24153 if(!this.activated){
24159 var r = this.doc.selection.createRange();
24170 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24174 // from jquery ui (MIT licenced)
24176 var win = this.win;
24178 if (win.getSelection && win.getSelection().getRangeAt) {
24179 range = win.getSelection().getRangeAt(0);
24180 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24181 range.insertNode(node);
24182 } else if (win.document.selection && win.document.selection.createRange) {
24183 // no firefox support
24184 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24185 win.document.selection.createRange().pasteHTML(txt);
24187 // no firefox support
24188 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24189 this.execCmd('InsertHTML', txt);
24198 mozKeyPress : function(e){
24200 var c = e.getCharCode(), cmd;
24203 c = String.fromCharCode(c).toLowerCase();
24217 this.cleanUpPaste.defer(100, this);
24225 e.preventDefault();
24233 fixKeys : function(){ // load time branching for fastest keydown performance
24235 return function(e){
24236 var k = e.getKey(), r;
24239 r = this.doc.selection.createRange();
24242 r.pasteHTML('    ');
24249 r = this.doc.selection.createRange();
24251 var target = r.parentElement();
24252 if(!target || target.tagName.toLowerCase() != 'li'){
24254 r.pasteHTML('<br />');
24260 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24261 this.cleanUpPaste.defer(100, this);
24267 }else if(Roo.isOpera){
24268 return function(e){
24269 var k = e.getKey();
24273 this.execCmd('InsertHTML','    ');
24276 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24277 this.cleanUpPaste.defer(100, this);
24282 }else if(Roo.isSafari){
24283 return function(e){
24284 var k = e.getKey();
24288 this.execCmd('InsertText','\t');
24292 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24293 this.cleanUpPaste.defer(100, this);
24301 getAllAncestors: function()
24303 var p = this.getSelectedNode();
24306 a.push(p); // push blank onto stack..
24307 p = this.getParentElement();
24311 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24315 a.push(this.doc.body);
24319 lastSelNode : false,
24322 getSelection : function()
24324 this.assignDocWin();
24325 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24328 getSelectedNode: function()
24330 // this may only work on Gecko!!!
24332 // should we cache this!!!!
24337 var range = this.createRange(this.getSelection()).cloneRange();
24340 var parent = range.parentElement();
24342 var testRange = range.duplicate();
24343 testRange.moveToElementText(parent);
24344 if (testRange.inRange(range)) {
24347 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24350 parent = parent.parentElement;
24355 // is ancestor a text element.
24356 var ac = range.commonAncestorContainer;
24357 if (ac.nodeType == 3) {
24358 ac = ac.parentNode;
24361 var ar = ac.childNodes;
24364 var other_nodes = [];
24365 var has_other_nodes = false;
24366 for (var i=0;i<ar.length;i++) {
24367 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24370 // fullly contained node.
24372 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24377 // probably selected..
24378 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24379 other_nodes.push(ar[i]);
24383 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24388 has_other_nodes = true;
24390 if (!nodes.length && other_nodes.length) {
24391 nodes= other_nodes;
24393 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24399 createRange: function(sel)
24401 // this has strange effects when using with
24402 // top toolbar - not sure if it's a great idea.
24403 //this.editor.contentWindow.focus();
24404 if (typeof sel != "undefined") {
24406 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24408 return this.doc.createRange();
24411 return this.doc.createRange();
24414 getParentElement: function()
24417 this.assignDocWin();
24418 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24420 var range = this.createRange(sel);
24423 var p = range.commonAncestorContainer;
24424 while (p.nodeType == 3) { // text node
24435 * Range intersection.. the hard stuff...
24439 * [ -- selected range --- ]
24443 * if end is before start or hits it. fail.
24444 * if start is after end or hits it fail.
24446 * if either hits (but other is outside. - then it's not
24452 // @see http://www.thismuchiknow.co.uk/?p=64.
24453 rangeIntersectsNode : function(range, node)
24455 var nodeRange = node.ownerDocument.createRange();
24457 nodeRange.selectNode(node);
24459 nodeRange.selectNodeContents(node);
24462 var rangeStartRange = range.cloneRange();
24463 rangeStartRange.collapse(true);
24465 var rangeEndRange = range.cloneRange();
24466 rangeEndRange.collapse(false);
24468 var nodeStartRange = nodeRange.cloneRange();
24469 nodeStartRange.collapse(true);
24471 var nodeEndRange = nodeRange.cloneRange();
24472 nodeEndRange.collapse(false);
24474 return rangeStartRange.compareBoundaryPoints(
24475 Range.START_TO_START, nodeEndRange) == -1 &&
24476 rangeEndRange.compareBoundaryPoints(
24477 Range.START_TO_START, nodeStartRange) == 1;
24481 rangeCompareNode : function(range, node)
24483 var nodeRange = node.ownerDocument.createRange();
24485 nodeRange.selectNode(node);
24487 nodeRange.selectNodeContents(node);
24491 range.collapse(true);
24493 nodeRange.collapse(true);
24495 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24496 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24498 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24500 var nodeIsBefore = ss == 1;
24501 var nodeIsAfter = ee == -1;
24503 if (nodeIsBefore && nodeIsAfter) {
24506 if (!nodeIsBefore && nodeIsAfter) {
24507 return 1; //right trailed.
24510 if (nodeIsBefore && !nodeIsAfter) {
24511 return 2; // left trailed.
24517 // private? - in a new class?
24518 cleanUpPaste : function()
24520 // cleans up the whole document..
24521 Roo.log('cleanuppaste');
24523 this.cleanUpChildren(this.doc.body);
24524 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24525 if (clean != this.doc.body.innerHTML) {
24526 this.doc.body.innerHTML = clean;
24531 cleanWordChars : function(input) {// change the chars to hex code
24532 var he = Roo.HtmlEditorCore;
24534 var output = input;
24535 Roo.each(he.swapCodes, function(sw) {
24536 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24538 output = output.replace(swapper, sw[1]);
24545 cleanUpChildren : function (n)
24547 if (!n.childNodes.length) {
24550 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24551 this.cleanUpChild(n.childNodes[i]);
24558 cleanUpChild : function (node)
24561 //console.log(node);
24562 if (node.nodeName == "#text") {
24563 // clean up silly Windows -- stuff?
24566 if (node.nodeName == "#comment") {
24567 node.parentNode.removeChild(node);
24568 // clean up silly Windows -- stuff?
24571 var lcname = node.tagName.toLowerCase();
24572 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24573 // whitelist of tags..
24575 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24577 node.parentNode.removeChild(node);
24582 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24584 // spans with no attributes - just remove them..
24585 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24586 remove_keep_children = true;
24589 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24590 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24592 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24593 // remove_keep_children = true;
24596 if (remove_keep_children) {
24597 this.cleanUpChildren(node);
24598 // inserts everything just before this node...
24599 while (node.childNodes.length) {
24600 var cn = node.childNodes[0];
24601 node.removeChild(cn);
24602 node.parentNode.insertBefore(cn, node);
24604 node.parentNode.removeChild(node);
24608 if (!node.attributes || !node.attributes.length) {
24613 this.cleanUpChildren(node);
24617 function cleanAttr(n,v)
24620 if (v.match(/^\./) || v.match(/^\//)) {
24623 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24626 if (v.match(/^#/)) {
24629 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24630 node.removeAttribute(n);
24634 var cwhite = this.cwhite;
24635 var cblack = this.cblack;
24637 function cleanStyle(n,v)
24639 if (v.match(/expression/)) { //XSS?? should we even bother..
24640 node.removeAttribute(n);
24644 var parts = v.split(/;/);
24647 Roo.each(parts, function(p) {
24648 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24652 var l = p.split(':').shift().replace(/\s+/g,'');
24653 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24655 if ( cwhite.length && cblack.indexOf(l) > -1) {
24656 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24657 //node.removeAttribute(n);
24661 // only allow 'c whitelisted system attributes'
24662 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24663 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24664 //node.removeAttribute(n);
24674 if (clean.length) {
24675 node.setAttribute(n, clean.join(';'));
24677 node.removeAttribute(n);
24683 for (var i = node.attributes.length-1; i > -1 ; i--) {
24684 var a = node.attributes[i];
24687 if (a.name.toLowerCase().substr(0,2)=='on') {
24688 node.removeAttribute(a.name);
24691 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24692 node.removeAttribute(a.name);
24695 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24696 cleanAttr(a.name,a.value); // fixme..
24699 if (a.name == 'style') {
24700 cleanStyle(a.name,a.value);
24703 /// clean up MS crap..
24704 // tecnically this should be a list of valid class'es..
24707 if (a.name == 'class') {
24708 if (a.value.match(/^Mso/)) {
24709 node.removeAttribute('class');
24712 if (a.value.match(/^body$/)) {
24713 node.removeAttribute('class');
24724 this.cleanUpChildren(node);
24730 * Clean up MS wordisms...
24732 cleanWord : function(node)
24735 this.cleanWord(this.doc.body);
24740 node.nodeName == 'SPAN' &&
24741 !node.hasAttributes() &&
24742 node.childNodes.length == 1 &&
24743 node.firstChild.nodeName == "#text"
24745 var textNode = node.firstChild;
24746 node.removeChild(textNode);
24747 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24748 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24750 node.parentNode.insertBefore(textNode, node);
24751 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24752 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24754 node.parentNode.removeChild(node);
24757 if (node.nodeName == "#text") {
24758 // clean up silly Windows -- stuff?
24761 if (node.nodeName == "#comment") {
24762 node.parentNode.removeChild(node);
24763 // clean up silly Windows -- stuff?
24767 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24768 node.parentNode.removeChild(node);
24771 //Roo.log(node.tagName);
24772 // remove - but keep children..
24773 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24774 //Roo.log('-- removed');
24775 while (node.childNodes.length) {
24776 var cn = node.childNodes[0];
24777 node.removeChild(cn);
24778 node.parentNode.insertBefore(cn, node);
24779 // move node to parent - and clean it..
24780 this.cleanWord(cn);
24782 node.parentNode.removeChild(node);
24783 /// no need to iterate chidlren = it's got none..
24784 //this.iterateChildren(node, this.cleanWord);
24788 if (node.className.length) {
24790 var cn = node.className.split(/\W+/);
24792 Roo.each(cn, function(cls) {
24793 if (cls.match(/Mso[a-zA-Z]+/)) {
24798 node.className = cna.length ? cna.join(' ') : '';
24800 node.removeAttribute("class");
24804 if (node.hasAttribute("lang")) {
24805 node.removeAttribute("lang");
24808 if (node.hasAttribute("style")) {
24810 var styles = node.getAttribute("style").split(";");
24812 Roo.each(styles, function(s) {
24813 if (!s.match(/:/)) {
24816 var kv = s.split(":");
24817 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24820 // what ever is left... we allow.
24823 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24824 if (!nstyle.length) {
24825 node.removeAttribute('style');
24828 this.iterateChildren(node, this.cleanWord);
24834 * iterateChildren of a Node, calling fn each time, using this as the scole..
24835 * @param {DomNode} node node to iterate children of.
24836 * @param {Function} fn method of this class to call on each item.
24838 iterateChildren : function(node, fn)
24840 if (!node.childNodes.length) {
24843 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24844 fn.call(this, node.childNodes[i])
24850 * cleanTableWidths.
24852 * Quite often pasting from word etc.. results in tables with column and widths.
24853 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24856 cleanTableWidths : function(node)
24861 this.cleanTableWidths(this.doc.body);
24866 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24869 Roo.log(node.tagName);
24870 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24871 this.iterateChildren(node, this.cleanTableWidths);
24874 if (node.hasAttribute('width')) {
24875 node.removeAttribute('width');
24879 if (node.hasAttribute("style")) {
24882 var styles = node.getAttribute("style").split(";");
24884 Roo.each(styles, function(s) {
24885 if (!s.match(/:/)) {
24888 var kv = s.split(":");
24889 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24892 // what ever is left... we allow.
24895 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24896 if (!nstyle.length) {
24897 node.removeAttribute('style');
24901 this.iterateChildren(node, this.cleanTableWidths);
24909 domToHTML : function(currentElement, depth, nopadtext) {
24911 depth = depth || 0;
24912 nopadtext = nopadtext || false;
24914 if (!currentElement) {
24915 return this.domToHTML(this.doc.body);
24918 //Roo.log(currentElement);
24920 var allText = false;
24921 var nodeName = currentElement.nodeName;
24922 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24924 if (nodeName == '#text') {
24926 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24931 if (nodeName != 'BODY') {
24934 // Prints the node tagName, such as <A>, <IMG>, etc
24937 for(i = 0; i < currentElement.attributes.length;i++) {
24939 var aname = currentElement.attributes.item(i).name;
24940 if (!currentElement.attributes.item(i).value.length) {
24943 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24946 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24955 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24958 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24963 // Traverse the tree
24965 var currentElementChild = currentElement.childNodes.item(i);
24966 var allText = true;
24967 var innerHTML = '';
24969 while (currentElementChild) {
24970 // Formatting code (indent the tree so it looks nice on the screen)
24971 var nopad = nopadtext;
24972 if (lastnode == 'SPAN') {
24976 if (currentElementChild.nodeName == '#text') {
24977 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24978 toadd = nopadtext ? toadd : toadd.trim();
24979 if (!nopad && toadd.length > 80) {
24980 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
24982 innerHTML += toadd;
24985 currentElementChild = currentElement.childNodes.item(i);
24991 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
24993 // Recursively traverse the tree structure of the child node
24994 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
24995 lastnode = currentElementChild.nodeName;
24997 currentElementChild=currentElement.childNodes.item(i);
25003 // The remaining code is mostly for formatting the tree
25004 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25009 ret+= "</"+tagName+">";
25015 applyBlacklists : function()
25017 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25018 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25022 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25023 if (b.indexOf(tag) > -1) {
25026 this.white.push(tag);
25030 Roo.each(w, function(tag) {
25031 if (b.indexOf(tag) > -1) {
25034 if (this.white.indexOf(tag) > -1) {
25037 this.white.push(tag);
25042 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25043 if (w.indexOf(tag) > -1) {
25046 this.black.push(tag);
25050 Roo.each(b, function(tag) {
25051 if (w.indexOf(tag) > -1) {
25054 if (this.black.indexOf(tag) > -1) {
25057 this.black.push(tag);
25062 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25063 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25067 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25068 if (b.indexOf(tag) > -1) {
25071 this.cwhite.push(tag);
25075 Roo.each(w, function(tag) {
25076 if (b.indexOf(tag) > -1) {
25079 if (this.cwhite.indexOf(tag) > -1) {
25082 this.cwhite.push(tag);
25087 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25088 if (w.indexOf(tag) > -1) {
25091 this.cblack.push(tag);
25095 Roo.each(b, function(tag) {
25096 if (w.indexOf(tag) > -1) {
25099 if (this.cblack.indexOf(tag) > -1) {
25102 this.cblack.push(tag);
25107 setStylesheets : function(stylesheets)
25109 if(typeof(stylesheets) == 'string'){
25110 Roo.get(this.iframe.contentDocument.head).createChild({
25112 rel : 'stylesheet',
25121 Roo.each(stylesheets, function(s) {
25126 Roo.get(_this.iframe.contentDocument.head).createChild({
25128 rel : 'stylesheet',
25137 removeStylesheets : function()
25141 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25146 setStyle : function(style)
25148 Roo.get(this.iframe.contentDocument.head).createChild({
25157 // hide stuff that is not compatible
25171 * @event specialkey
25175 * @cfg {String} fieldClass @hide
25178 * @cfg {String} focusClass @hide
25181 * @cfg {String} autoCreate @hide
25184 * @cfg {String} inputType @hide
25187 * @cfg {String} invalidClass @hide
25190 * @cfg {String} invalidText @hide
25193 * @cfg {String} msgFx @hide
25196 * @cfg {String} validateOnBlur @hide
25200 Roo.HtmlEditorCore.white = [
25201 'area', 'br', 'img', 'input', 'hr', 'wbr',
25203 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25204 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25205 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25206 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25207 'table', 'ul', 'xmp',
25209 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25212 'dir', 'menu', 'ol', 'ul', 'dl',
25218 Roo.HtmlEditorCore.black = [
25219 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25221 'base', 'basefont', 'bgsound', 'blink', 'body',
25222 'frame', 'frameset', 'head', 'html', 'ilayer',
25223 'iframe', 'layer', 'link', 'meta', 'object',
25224 'script', 'style' ,'title', 'xml' // clean later..
25226 Roo.HtmlEditorCore.clean = [
25227 'script', 'style', 'title', 'xml'
25229 Roo.HtmlEditorCore.remove = [
25234 Roo.HtmlEditorCore.ablack = [
25238 Roo.HtmlEditorCore.aclean = [
25239 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25243 Roo.HtmlEditorCore.pwhite= [
25244 'http', 'https', 'mailto'
25247 // white listed style attributes.
25248 Roo.HtmlEditorCore.cwhite= [
25249 // 'text-align', /// default is to allow most things..
25255 // black listed style attributes.
25256 Roo.HtmlEditorCore.cblack= [
25257 // 'font-size' -- this can be set by the project
25261 Roo.HtmlEditorCore.swapCodes =[
25280 * @class Roo.bootstrap.HtmlEditor
25281 * @extends Roo.bootstrap.TextArea
25282 * Bootstrap HtmlEditor class
25285 * Create a new HtmlEditor
25286 * @param {Object} config The config object
25289 Roo.bootstrap.HtmlEditor = function(config){
25290 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25291 if (!this.toolbars) {
25292 this.toolbars = [];
25295 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25298 * @event initialize
25299 * Fires when the editor is fully initialized (including the iframe)
25300 * @param {HtmlEditor} this
25305 * Fires when the editor is first receives the focus. Any insertion must wait
25306 * until after this event.
25307 * @param {HtmlEditor} this
25311 * @event beforesync
25312 * Fires before the textarea is updated with content from the editor iframe. Return false
25313 * to cancel the sync.
25314 * @param {HtmlEditor} this
25315 * @param {String} html
25319 * @event beforepush
25320 * Fires before the iframe editor is updated with content from the textarea. Return false
25321 * to cancel the push.
25322 * @param {HtmlEditor} this
25323 * @param {String} html
25328 * Fires when the textarea is updated with content from the editor iframe.
25329 * @param {HtmlEditor} this
25330 * @param {String} html
25335 * Fires when the iframe editor is updated with content from the textarea.
25336 * @param {HtmlEditor} this
25337 * @param {String} html
25341 * @event editmodechange
25342 * Fires when the editor switches edit modes
25343 * @param {HtmlEditor} this
25344 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25346 editmodechange: true,
25348 * @event editorevent
25349 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25350 * @param {HtmlEditor} this
25354 * @event firstfocus
25355 * Fires when on first focus - needed by toolbars..
25356 * @param {HtmlEditor} this
25361 * Auto save the htmlEditor value as a file into Events
25362 * @param {HtmlEditor} this
25366 * @event savedpreview
25367 * preview the saved version of htmlEditor
25368 * @param {HtmlEditor} this
25375 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25379 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25384 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25389 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25394 * @cfg {Number} height (in pixels)
25398 * @cfg {Number} width (in pixels)
25403 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25406 stylesheets: false,
25411 // private properties
25412 validationEvent : false,
25414 initialized : false,
25417 onFocus : Roo.emptyFn,
25419 hideMode:'offsets',
25421 tbContainer : false,
25425 toolbarContainer :function() {
25426 return this.wrap.select('.x-html-editor-tb',true).first();
25430 * Protected method that will not generally be called directly. It
25431 * is called when the editor creates its toolbar. Override this method if you need to
25432 * add custom toolbar buttons.
25433 * @param {HtmlEditor} editor
25435 createToolbar : function(){
25436 Roo.log('renewing');
25437 Roo.log("create toolbars");
25439 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25440 this.toolbars[0].render(this.toolbarContainer());
25444 // if (!editor.toolbars || !editor.toolbars.length) {
25445 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25448 // for (var i =0 ; i < editor.toolbars.length;i++) {
25449 // editor.toolbars[i] = Roo.factory(
25450 // typeof(editor.toolbars[i]) == 'string' ?
25451 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25452 // Roo.bootstrap.HtmlEditor);
25453 // editor.toolbars[i].init(editor);
25459 onRender : function(ct, position)
25461 // Roo.log("Call onRender: " + this.xtype);
25463 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25465 this.wrap = this.inputEl().wrap({
25466 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25469 this.editorcore.onRender(ct, position);
25471 if (this.resizable) {
25472 this.resizeEl = new Roo.Resizable(this.wrap, {
25476 minHeight : this.height,
25477 height: this.height,
25478 handles : this.resizable,
25481 resize : function(r, w, h) {
25482 _t.onResize(w,h); // -something
25488 this.createToolbar(this);
25491 if(!this.width && this.resizable){
25492 this.setSize(this.wrap.getSize());
25494 if (this.resizeEl) {
25495 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25496 // should trigger onReize..
25502 onResize : function(w, h)
25504 Roo.log('resize: ' +w + ',' + h );
25505 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25509 if(this.inputEl() ){
25510 if(typeof w == 'number'){
25511 var aw = w - this.wrap.getFrameWidth('lr');
25512 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25515 if(typeof h == 'number'){
25516 var tbh = -11; // fixme it needs to tool bar size!
25517 for (var i =0; i < this.toolbars.length;i++) {
25518 // fixme - ask toolbars for heights?
25519 tbh += this.toolbars[i].el.getHeight();
25520 //if (this.toolbars[i].footer) {
25521 // tbh += this.toolbars[i].footer.el.getHeight();
25529 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25530 ah -= 5; // knock a few pixes off for look..
25531 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25535 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25536 this.editorcore.onResize(ew,eh);
25541 * Toggles the editor between standard and source edit mode.
25542 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25544 toggleSourceEdit : function(sourceEditMode)
25546 this.editorcore.toggleSourceEdit(sourceEditMode);
25548 if(this.editorcore.sourceEditMode){
25549 Roo.log('editor - showing textarea');
25552 // Roo.log(this.syncValue());
25554 this.inputEl().removeClass(['hide', 'x-hidden']);
25555 this.inputEl().dom.removeAttribute('tabIndex');
25556 this.inputEl().focus();
25558 Roo.log('editor - hiding textarea');
25560 // Roo.log(this.pushValue());
25563 this.inputEl().addClass(['hide', 'x-hidden']);
25564 this.inputEl().dom.setAttribute('tabIndex', -1);
25565 //this.deferFocus();
25568 if(this.resizable){
25569 this.setSize(this.wrap.getSize());
25572 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25575 // private (for BoxComponent)
25576 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25578 // private (for BoxComponent)
25579 getResizeEl : function(){
25583 // private (for BoxComponent)
25584 getPositionEl : function(){
25589 initEvents : function(){
25590 this.originalValue = this.getValue();
25594 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25597 // markInvalid : Roo.emptyFn,
25599 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25602 // clearInvalid : Roo.emptyFn,
25604 setValue : function(v){
25605 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25606 this.editorcore.pushValue();
25611 deferFocus : function(){
25612 this.focus.defer(10, this);
25616 focus : function(){
25617 this.editorcore.focus();
25623 onDestroy : function(){
25629 for (var i =0; i < this.toolbars.length;i++) {
25630 // fixme - ask toolbars for heights?
25631 this.toolbars[i].onDestroy();
25634 this.wrap.dom.innerHTML = '';
25635 this.wrap.remove();
25640 onFirstFocus : function(){
25641 //Roo.log("onFirstFocus");
25642 this.editorcore.onFirstFocus();
25643 for (var i =0; i < this.toolbars.length;i++) {
25644 this.toolbars[i].onFirstFocus();
25650 syncValue : function()
25652 this.editorcore.syncValue();
25655 pushValue : function()
25657 this.editorcore.pushValue();
25661 // hide stuff that is not compatible
25675 * @event specialkey
25679 * @cfg {String} fieldClass @hide
25682 * @cfg {String} focusClass @hide
25685 * @cfg {String} autoCreate @hide
25688 * @cfg {String} inputType @hide
25692 * @cfg {String} invalidText @hide
25695 * @cfg {String} msgFx @hide
25698 * @cfg {String} validateOnBlur @hide
25707 Roo.namespace('Roo.bootstrap.htmleditor');
25709 * @class Roo.bootstrap.HtmlEditorToolbar1
25715 new Roo.bootstrap.HtmlEditor({
25718 new Roo.bootstrap.HtmlEditorToolbar1({
25719 disable : { fonts: 1 , format: 1, ..., ... , ...],
25725 * @cfg {Object} disable List of elements to disable..
25726 * @cfg {Array} btns List of additional buttons.
25730 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25733 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25736 Roo.apply(this, config);
25738 // default disabled, based on 'good practice'..
25739 this.disable = this.disable || {};
25740 Roo.applyIf(this.disable, {
25743 specialElements : true
25745 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25747 this.editor = config.editor;
25748 this.editorcore = config.editor.editorcore;
25750 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25752 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25753 // dont call parent... till later.
25755 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25760 editorcore : false,
25765 "h1","h2","h3","h4","h5","h6",
25767 "abbr", "acronym", "address", "cite", "samp", "var",
25771 onRender : function(ct, position)
25773 // Roo.log("Call onRender: " + this.xtype);
25775 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25777 this.el.dom.style.marginBottom = '0';
25779 var editorcore = this.editorcore;
25780 var editor= this.editor;
25783 var btn = function(id,cmd , toggle, handler, html){
25785 var event = toggle ? 'toggle' : 'click';
25790 xns: Roo.bootstrap,
25794 enableToggle:toggle !== false,
25796 pressed : toggle ? false : null,
25799 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25800 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25806 // var cb_box = function...
25811 xns: Roo.bootstrap,
25816 xns: Roo.bootstrap,
25820 Roo.each(this.formats, function(f) {
25821 style.menu.items.push({
25823 xns: Roo.bootstrap,
25824 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25829 editorcore.insertTag(this.tagname);
25836 children.push(style);
25838 btn('bold',false,true);
25839 btn('italic',false,true);
25840 btn('align-left', 'justifyleft',true);
25841 btn('align-center', 'justifycenter',true);
25842 btn('align-right' , 'justifyright',true);
25843 btn('link', false, false, function(btn) {
25844 //Roo.log("create link?");
25845 var url = prompt(this.createLinkText, this.defaultLinkValue);
25846 if(url && url != 'http:/'+'/'){
25847 this.editorcore.relayCmd('createlink', url);
25850 btn('list','insertunorderedlist',true);
25851 btn('pencil', false,true, function(btn){
25853 this.toggleSourceEdit(btn.pressed);
25856 if (this.editor.btns.length > 0) {
25857 for (var i = 0; i<this.editor.btns.length; i++) {
25858 children.push(this.editor.btns[i]);
25866 xns: Roo.bootstrap,
25871 xns: Roo.bootstrap,
25876 cog.menu.items.push({
25878 xns: Roo.bootstrap,
25879 html : Clean styles,
25884 editorcore.insertTag(this.tagname);
25893 this.xtype = 'NavSimplebar';
25895 for(var i=0;i< children.length;i++) {
25897 this.buttons.add(this.addxtypeChild(children[i]));
25901 editor.on('editorevent', this.updateToolbar, this);
25903 onBtnClick : function(id)
25905 this.editorcore.relayCmd(id);
25906 this.editorcore.focus();
25910 * Protected method that will not generally be called directly. It triggers
25911 * a toolbar update by reading the markup state of the current selection in the editor.
25913 updateToolbar: function(){
25915 if(!this.editorcore.activated){
25916 this.editor.onFirstFocus(); // is this neeed?
25920 var btns = this.buttons;
25921 var doc = this.editorcore.doc;
25922 btns.get('bold').setActive(doc.queryCommandState('bold'));
25923 btns.get('italic').setActive(doc.queryCommandState('italic'));
25924 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25926 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25927 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25928 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25930 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25931 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25934 var ans = this.editorcore.getAllAncestors();
25935 if (this.formatCombo) {
25938 var store = this.formatCombo.store;
25939 this.formatCombo.setValue("");
25940 for (var i =0; i < ans.length;i++) {
25941 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25943 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25951 // hides menus... - so this cant be on a menu...
25952 Roo.bootstrap.MenuMgr.hideAll();
25954 Roo.bootstrap.MenuMgr.hideAll();
25955 //this.editorsyncValue();
25957 onFirstFocus: function() {
25958 this.buttons.each(function(item){
25962 toggleSourceEdit : function(sourceEditMode){
25965 if(sourceEditMode){
25966 Roo.log("disabling buttons");
25967 this.buttons.each( function(item){
25968 if(item.cmd != 'pencil'){
25974 Roo.log("enabling buttons");
25975 if(this.editorcore.initialized){
25976 this.buttons.each( function(item){
25982 Roo.log("calling toggole on editor");
25983 // tell the editor that it's been pressed..
25984 this.editor.toggleSourceEdit(sourceEditMode);
25994 * @class Roo.bootstrap.Table.AbstractSelectionModel
25995 * @extends Roo.util.Observable
25996 * Abstract base class for grid SelectionModels. It provides the interface that should be
25997 * implemented by descendant classes. This class should not be directly instantiated.
26000 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26001 this.locked = false;
26002 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26006 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26007 /** @ignore Called by the grid automatically. Do not call directly. */
26008 init : function(grid){
26014 * Locks the selections.
26017 this.locked = true;
26021 * Unlocks the selections.
26023 unlock : function(){
26024 this.locked = false;
26028 * Returns true if the selections are locked.
26029 * @return {Boolean}
26031 isLocked : function(){
26032 return this.locked;
26036 initEvents : function ()
26042 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26043 * @class Roo.bootstrap.Table.RowSelectionModel
26044 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26045 * It supports multiple selections and keyboard selection/navigation.
26047 * @param {Object} config
26050 Roo.bootstrap.Table.RowSelectionModel = function(config){
26051 Roo.apply(this, config);
26052 this.selections = new Roo.util.MixedCollection(false, function(o){
26057 this.lastActive = false;
26061 * @event selectionchange
26062 * Fires when the selection changes
26063 * @param {SelectionModel} this
26065 "selectionchange" : true,
26067 * @event afterselectionchange
26068 * Fires after the selection changes (eg. by key press or clicking)
26069 * @param {SelectionModel} this
26071 "afterselectionchange" : true,
26073 * @event beforerowselect
26074 * Fires when a row is selected being selected, return false to cancel.
26075 * @param {SelectionModel} this
26076 * @param {Number} rowIndex The selected index
26077 * @param {Boolean} keepExisting False if other selections will be cleared
26079 "beforerowselect" : true,
26082 * Fires when a row is selected.
26083 * @param {SelectionModel} this
26084 * @param {Number} rowIndex The selected index
26085 * @param {Roo.data.Record} r The record
26087 "rowselect" : true,
26089 * @event rowdeselect
26090 * Fires when a row is deselected.
26091 * @param {SelectionModel} this
26092 * @param {Number} rowIndex The selected index
26094 "rowdeselect" : true
26096 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26097 this.locked = false;
26100 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26102 * @cfg {Boolean} singleSelect
26103 * True to allow selection of only one row at a time (defaults to false)
26105 singleSelect : false,
26108 initEvents : function()
26111 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26112 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26113 //}else{ // allow click to work like normal
26114 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26116 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26117 this.grid.on("rowclick", this.handleMouseDown, this);
26119 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26120 "up" : function(e){
26122 this.selectPrevious(e.shiftKey);
26123 }else if(this.last !== false && this.lastActive !== false){
26124 var last = this.last;
26125 this.selectRange(this.last, this.lastActive-1);
26126 this.grid.getView().focusRow(this.lastActive);
26127 if(last !== false){
26131 this.selectFirstRow();
26133 this.fireEvent("afterselectionchange", this);
26135 "down" : function(e){
26137 this.selectNext(e.shiftKey);
26138 }else if(this.last !== false && this.lastActive !== false){
26139 var last = this.last;
26140 this.selectRange(this.last, this.lastActive+1);
26141 this.grid.getView().focusRow(this.lastActive);
26142 if(last !== false){
26146 this.selectFirstRow();
26148 this.fireEvent("afterselectionchange", this);
26152 this.grid.store.on('load', function(){
26153 this.selections.clear();
26156 var view = this.grid.view;
26157 view.on("refresh", this.onRefresh, this);
26158 view.on("rowupdated", this.onRowUpdated, this);
26159 view.on("rowremoved", this.onRemove, this);
26164 onRefresh : function()
26166 var ds = this.grid.store, i, v = this.grid.view;
26167 var s = this.selections;
26168 s.each(function(r){
26169 if((i = ds.indexOfId(r.id)) != -1){
26178 onRemove : function(v, index, r){
26179 this.selections.remove(r);
26183 onRowUpdated : function(v, index, r){
26184 if(this.isSelected(r)){
26185 v.onRowSelect(index);
26191 * @param {Array} records The records to select
26192 * @param {Boolean} keepExisting (optional) True to keep existing selections
26194 selectRecords : function(records, keepExisting)
26197 this.clearSelections();
26199 var ds = this.grid.store;
26200 for(var i = 0, len = records.length; i < len; i++){
26201 this.selectRow(ds.indexOf(records[i]), true);
26206 * Gets the number of selected rows.
26209 getCount : function(){
26210 return this.selections.length;
26214 * Selects the first row in the grid.
26216 selectFirstRow : function(){
26221 * Select the last row.
26222 * @param {Boolean} keepExisting (optional) True to keep existing selections
26224 selectLastRow : function(keepExisting){
26225 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26226 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26230 * Selects the row immediately following the last selected row.
26231 * @param {Boolean} keepExisting (optional) True to keep existing selections
26233 selectNext : function(keepExisting)
26235 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26236 this.selectRow(this.last+1, keepExisting);
26237 this.grid.getView().focusRow(this.last);
26242 * Selects the row that precedes the last selected row.
26243 * @param {Boolean} keepExisting (optional) True to keep existing selections
26245 selectPrevious : function(keepExisting){
26247 this.selectRow(this.last-1, keepExisting);
26248 this.grid.getView().focusRow(this.last);
26253 * Returns the selected records
26254 * @return {Array} Array of selected records
26256 getSelections : function(){
26257 return [].concat(this.selections.items);
26261 * Returns the first selected record.
26264 getSelected : function(){
26265 return this.selections.itemAt(0);
26270 * Clears all selections.
26272 clearSelections : function(fast)
26278 var ds = this.grid.store;
26279 var s = this.selections;
26280 s.each(function(r){
26281 this.deselectRow(ds.indexOfId(r.id));
26285 this.selections.clear();
26292 * Selects all rows.
26294 selectAll : function(){
26298 this.selections.clear();
26299 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26300 this.selectRow(i, true);
26305 * Returns True if there is a selection.
26306 * @return {Boolean}
26308 hasSelection : function(){
26309 return this.selections.length > 0;
26313 * Returns True if the specified row is selected.
26314 * @param {Number/Record} record The record or index of the record to check
26315 * @return {Boolean}
26317 isSelected : function(index){
26318 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26319 return (r && this.selections.key(r.id) ? true : false);
26323 * Returns True if the specified record id is selected.
26324 * @param {String} id The id of record to check
26325 * @return {Boolean}
26327 isIdSelected : function(id){
26328 return (this.selections.key(id) ? true : false);
26333 handleMouseDBClick : function(e, t){
26337 handleMouseDown : function(e, t)
26339 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26340 if(this.isLocked() || rowIndex < 0 ){
26343 if(e.shiftKey && this.last !== false){
26344 var last = this.last;
26345 this.selectRange(last, rowIndex, e.ctrlKey);
26346 this.last = last; // reset the last
26350 var isSelected = this.isSelected(rowIndex);
26351 //Roo.log("select row:" + rowIndex);
26353 this.deselectRow(rowIndex);
26355 this.selectRow(rowIndex, true);
26359 if(e.button !== 0 && isSelected){
26360 alert('rowIndex 2: ' + rowIndex);
26361 view.focusRow(rowIndex);
26362 }else if(e.ctrlKey && isSelected){
26363 this.deselectRow(rowIndex);
26364 }else if(!isSelected){
26365 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26366 view.focusRow(rowIndex);
26370 this.fireEvent("afterselectionchange", this);
26373 handleDragableRowClick : function(grid, rowIndex, e)
26375 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26376 this.selectRow(rowIndex, false);
26377 grid.view.focusRow(rowIndex);
26378 this.fireEvent("afterselectionchange", this);
26383 * Selects multiple rows.
26384 * @param {Array} rows Array of the indexes of the row to select
26385 * @param {Boolean} keepExisting (optional) True to keep existing selections
26387 selectRows : function(rows, keepExisting){
26389 this.clearSelections();
26391 for(var i = 0, len = rows.length; i < len; i++){
26392 this.selectRow(rows[i], true);
26397 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26398 * @param {Number} startRow The index of the first row in the range
26399 * @param {Number} endRow The index of the last row in the range
26400 * @param {Boolean} keepExisting (optional) True to retain existing selections
26402 selectRange : function(startRow, endRow, keepExisting){
26407 this.clearSelections();
26409 if(startRow <= endRow){
26410 for(var i = startRow; i <= endRow; i++){
26411 this.selectRow(i, true);
26414 for(var i = startRow; i >= endRow; i--){
26415 this.selectRow(i, true);
26421 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26422 * @param {Number} startRow The index of the first row in the range
26423 * @param {Number} endRow The index of the last row in the range
26425 deselectRange : function(startRow, endRow, preventViewNotify){
26429 for(var i = startRow; i <= endRow; i++){
26430 this.deselectRow(i, preventViewNotify);
26436 * @param {Number} row The index of the row to select
26437 * @param {Boolean} keepExisting (optional) True to keep existing selections
26439 selectRow : function(index, keepExisting, preventViewNotify)
26441 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26444 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26445 if(!keepExisting || this.singleSelect){
26446 this.clearSelections();
26449 var r = this.grid.store.getAt(index);
26450 //console.log('selectRow - record id :' + r.id);
26452 this.selections.add(r);
26453 this.last = this.lastActive = index;
26454 if(!preventViewNotify){
26455 var proxy = new Roo.Element(
26456 this.grid.getRowDom(index)
26458 proxy.addClass('bg-info info');
26460 this.fireEvent("rowselect", this, index, r);
26461 this.fireEvent("selectionchange", this);
26467 * @param {Number} row The index of the row to deselect
26469 deselectRow : function(index, preventViewNotify)
26474 if(this.last == index){
26477 if(this.lastActive == index){
26478 this.lastActive = false;
26481 var r = this.grid.store.getAt(index);
26486 this.selections.remove(r);
26487 //.console.log('deselectRow - record id :' + r.id);
26488 if(!preventViewNotify){
26490 var proxy = new Roo.Element(
26491 this.grid.getRowDom(index)
26493 proxy.removeClass('bg-info info');
26495 this.fireEvent("rowdeselect", this, index);
26496 this.fireEvent("selectionchange", this);
26500 restoreLast : function(){
26502 this.last = this._last;
26507 acceptsNav : function(row, col, cm){
26508 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26512 onEditorKey : function(field, e){
26513 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26518 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26520 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26522 }else if(k == e.ENTER && !e.ctrlKey){
26526 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26528 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26530 }else if(k == e.ESC){
26534 g.startEditing(newCell[0], newCell[1]);
26540 * Ext JS Library 1.1.1
26541 * Copyright(c) 2006-2007, Ext JS, LLC.
26543 * Originally Released Under LGPL - original licence link has changed is not relivant.
26546 * <script type="text/javascript">
26550 * @class Roo.bootstrap.PagingToolbar
26551 * @extends Roo.bootstrap.NavSimplebar
26552 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26554 * Create a new PagingToolbar
26555 * @param {Object} config The config object
26556 * @param {Roo.data.Store} store
26558 Roo.bootstrap.PagingToolbar = function(config)
26560 // old args format still supported... - xtype is prefered..
26561 // created from xtype...
26563 this.ds = config.dataSource;
26565 if (config.store && !this.ds) {
26566 this.store= Roo.factory(config.store, Roo.data);
26567 this.ds = this.store;
26568 this.ds.xmodule = this.xmodule || false;
26571 this.toolbarItems = [];
26572 if (config.items) {
26573 this.toolbarItems = config.items;
26576 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26581 this.bind(this.ds);
26584 if (Roo.bootstrap.version == 4) {
26585 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26587 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26592 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26594 * @cfg {Roo.data.Store} dataSource
26595 * The underlying data store providing the paged data
26598 * @cfg {String/HTMLElement/Element} container
26599 * container The id or element that will contain the toolbar
26602 * @cfg {Boolean} displayInfo
26603 * True to display the displayMsg (defaults to false)
26606 * @cfg {Number} pageSize
26607 * The number of records to display per page (defaults to 20)
26611 * @cfg {String} displayMsg
26612 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26614 displayMsg : 'Displaying {0} - {1} of {2}',
26616 * @cfg {String} emptyMsg
26617 * The message to display when no records are found (defaults to "No data to display")
26619 emptyMsg : 'No data to display',
26621 * Customizable piece of the default paging text (defaults to "Page")
26624 beforePageText : "Page",
26626 * Customizable piece of the default paging text (defaults to "of %0")
26629 afterPageText : "of {0}",
26631 * Customizable piece of the default paging text (defaults to "First Page")
26634 firstText : "First Page",
26636 * Customizable piece of the default paging text (defaults to "Previous Page")
26639 prevText : "Previous Page",
26641 * Customizable piece of the default paging text (defaults to "Next Page")
26644 nextText : "Next Page",
26646 * Customizable piece of the default paging text (defaults to "Last Page")
26649 lastText : "Last Page",
26651 * Customizable piece of the default paging text (defaults to "Refresh")
26654 refreshText : "Refresh",
26658 onRender : function(ct, position)
26660 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26661 this.navgroup.parentId = this.id;
26662 this.navgroup.onRender(this.el, null);
26663 // add the buttons to the navgroup
26665 if(this.displayInfo){
26666 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26667 this.displayEl = this.el.select('.x-paging-info', true).first();
26668 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26669 // this.displayEl = navel.el.select('span',true).first();
26675 Roo.each(_this.buttons, function(e){ // this might need to use render????
26676 Roo.factory(e).render(_this.el);
26680 Roo.each(_this.toolbarItems, function(e) {
26681 _this.navgroup.addItem(e);
26685 this.first = this.navgroup.addItem({
26686 tooltip: this.firstText,
26687 cls: "prev btn-outline-secondary",
26688 html : ' <i class="fa fa-step-backward"></i>',
26690 preventDefault: true,
26691 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26694 this.prev = this.navgroup.addItem({
26695 tooltip: this.prevText,
26696 cls: "prev btn-outline-secondary",
26697 html : ' <i class="fa fa-backward"></i>',
26699 preventDefault: true,
26700 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26702 //this.addSeparator();
26705 var field = this.navgroup.addItem( {
26707 cls : 'x-paging-position btn-outline-secondary',
26709 html : this.beforePageText +
26710 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26711 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26714 this.field = field.el.select('input', true).first();
26715 this.field.on("keydown", this.onPagingKeydown, this);
26716 this.field.on("focus", function(){this.dom.select();});
26719 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26720 //this.field.setHeight(18);
26721 //this.addSeparator();
26722 this.next = this.navgroup.addItem({
26723 tooltip: this.nextText,
26724 cls: "next btn-outline-secondary",
26725 html : ' <i class="fa fa-forward"></i>',
26727 preventDefault: true,
26728 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26730 this.last = this.navgroup.addItem({
26731 tooltip: this.lastText,
26732 html : ' <i class="fa fa-step-forward"></i>',
26733 cls: "next btn-outline-secondary",
26735 preventDefault: true,
26736 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26738 //this.addSeparator();
26739 this.loading = this.navgroup.addItem({
26740 tooltip: this.refreshText,
26741 cls: "btn-outline-secondary",
26742 html : ' <i class="fa fa-refresh"></i>',
26743 preventDefault: true,
26744 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26750 updateInfo : function(){
26751 if(this.displayEl){
26752 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26753 var msg = count == 0 ?
26757 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26759 this.displayEl.update(msg);
26764 onLoad : function(ds, r, o)
26766 this.cursor = o.params.start ? o.params.start : 0;
26768 var d = this.getPageData(),
26773 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26774 this.field.dom.value = ap;
26775 this.first.setDisabled(ap == 1);
26776 this.prev.setDisabled(ap == 1);
26777 this.next.setDisabled(ap == ps);
26778 this.last.setDisabled(ap == ps);
26779 this.loading.enable();
26784 getPageData : function(){
26785 var total = this.ds.getTotalCount();
26788 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26789 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26794 onLoadError : function(){
26795 this.loading.enable();
26799 onPagingKeydown : function(e){
26800 var k = e.getKey();
26801 var d = this.getPageData();
26803 var v = this.field.dom.value, pageNum;
26804 if(!v || isNaN(pageNum = parseInt(v, 10))){
26805 this.field.dom.value = d.activePage;
26808 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26809 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26812 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))
26814 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26815 this.field.dom.value = pageNum;
26816 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26819 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26821 var v = this.field.dom.value, pageNum;
26822 var increment = (e.shiftKey) ? 10 : 1;
26823 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26826 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26827 this.field.dom.value = d.activePage;
26830 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26832 this.field.dom.value = parseInt(v, 10) + increment;
26833 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26834 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26841 beforeLoad : function(){
26843 this.loading.disable();
26848 onClick : function(which){
26857 ds.load({params:{start: 0, limit: this.pageSize}});
26860 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26863 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26866 var total = ds.getTotalCount();
26867 var extra = total % this.pageSize;
26868 var lastStart = extra ? (total - extra) : total-this.pageSize;
26869 ds.load({params:{start: lastStart, limit: this.pageSize}});
26872 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26878 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26879 * @param {Roo.data.Store} store The data store to unbind
26881 unbind : function(ds){
26882 ds.un("beforeload", this.beforeLoad, this);
26883 ds.un("load", this.onLoad, this);
26884 ds.un("loadexception", this.onLoadError, this);
26885 ds.un("remove", this.updateInfo, this);
26886 ds.un("add", this.updateInfo, this);
26887 this.ds = undefined;
26891 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26892 * @param {Roo.data.Store} store The data store to bind
26894 bind : function(ds){
26895 ds.on("beforeload", this.beforeLoad, this);
26896 ds.on("load", this.onLoad, this);
26897 ds.on("loadexception", this.onLoadError, this);
26898 ds.on("remove", this.updateInfo, this);
26899 ds.on("add", this.updateInfo, this);
26910 * @class Roo.bootstrap.MessageBar
26911 * @extends Roo.bootstrap.Component
26912 * Bootstrap MessageBar class
26913 * @cfg {String} html contents of the MessageBar
26914 * @cfg {String} weight (info | success | warning | danger) default info
26915 * @cfg {String} beforeClass insert the bar before the given class
26916 * @cfg {Boolean} closable (true | false) default false
26917 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26920 * Create a new Element
26921 * @param {Object} config The config object
26924 Roo.bootstrap.MessageBar = function(config){
26925 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26928 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26934 beforeClass: 'bootstrap-sticky-wrap',
26936 getAutoCreate : function(){
26940 cls: 'alert alert-dismissable alert-' + this.weight,
26945 html: this.html || ''
26951 cfg.cls += ' alert-messages-fixed';
26965 onRender : function(ct, position)
26967 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26970 var cfg = Roo.apply({}, this.getAutoCreate());
26974 cfg.cls += ' ' + this.cls;
26977 cfg.style = this.style;
26979 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26981 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26984 this.el.select('>button.close').on('click', this.hide, this);
26990 if (!this.rendered) {
26996 this.fireEvent('show', this);
27002 if (!this.rendered) {
27008 this.fireEvent('hide', this);
27011 update : function()
27013 // var e = this.el.dom.firstChild;
27015 // if(this.closable){
27016 // e = e.nextSibling;
27019 // e.data = this.html || '';
27021 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27037 * @class Roo.bootstrap.Graph
27038 * @extends Roo.bootstrap.Component
27039 * Bootstrap Graph class
27043 @cfg {String} graphtype bar | vbar | pie
27044 @cfg {number} g_x coodinator | centre x (pie)
27045 @cfg {number} g_y coodinator | centre y (pie)
27046 @cfg {number} g_r radius (pie)
27047 @cfg {number} g_height height of the chart (respected by all elements in the set)
27048 @cfg {number} g_width width of the chart (respected by all elements in the set)
27049 @cfg {Object} title The title of the chart
27052 -opts (object) options for the chart
27054 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27055 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27057 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.
27058 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27060 o stretch (boolean)
27062 -opts (object) options for the pie
27065 o startAngle (number)
27066 o endAngle (number)
27070 * Create a new Input
27071 * @param {Object} config The config object
27074 Roo.bootstrap.Graph = function(config){
27075 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27081 * The img click event for the img.
27082 * @param {Roo.EventObject} e
27088 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27099 //g_colors: this.colors,
27106 getAutoCreate : function(){
27117 onRender : function(ct,position){
27120 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27122 if (typeof(Raphael) == 'undefined') {
27123 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27127 this.raphael = Raphael(this.el.dom);
27129 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27130 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27131 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27132 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27134 r.text(160, 10, "Single Series Chart").attr(txtattr);
27135 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27136 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27137 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27139 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27140 r.barchart(330, 10, 300, 220, data1);
27141 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27142 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27145 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27146 // r.barchart(30, 30, 560, 250, xdata, {
27147 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27148 // axis : "0 0 1 1",
27149 // axisxlabels : xdata
27150 // //yvalues : cols,
27153 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27155 // this.load(null,xdata,{
27156 // axis : "0 0 1 1",
27157 // axisxlabels : xdata
27162 load : function(graphtype,xdata,opts)
27164 this.raphael.clear();
27166 graphtype = this.graphtype;
27171 var r = this.raphael,
27172 fin = function () {
27173 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27175 fout = function () {
27176 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27178 pfin = function() {
27179 this.sector.stop();
27180 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27183 this.label[0].stop();
27184 this.label[0].attr({ r: 7.5 });
27185 this.label[1].attr({ "font-weight": 800 });
27188 pfout = function() {
27189 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27192 this.label[0].animate({ r: 5 }, 500, "bounce");
27193 this.label[1].attr({ "font-weight": 400 });
27199 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27202 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27205 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27206 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27208 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27215 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27220 setTitle: function(o)
27225 initEvents: function() {
27228 this.el.on('click', this.onClick, this);
27232 onClick : function(e)
27234 Roo.log('img onclick');
27235 this.fireEvent('click', this, e);
27247 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27250 * @class Roo.bootstrap.dash.NumberBox
27251 * @extends Roo.bootstrap.Component
27252 * Bootstrap NumberBox class
27253 * @cfg {String} headline Box headline
27254 * @cfg {String} content Box content
27255 * @cfg {String} icon Box icon
27256 * @cfg {String} footer Footer text
27257 * @cfg {String} fhref Footer href
27260 * Create a new NumberBox
27261 * @param {Object} config The config object
27265 Roo.bootstrap.dash.NumberBox = function(config){
27266 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27270 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27279 getAutoCreate : function(){
27283 cls : 'small-box ',
27291 cls : 'roo-headline',
27292 html : this.headline
27296 cls : 'roo-content',
27297 html : this.content
27311 cls : 'ion ' + this.icon
27320 cls : 'small-box-footer',
27321 href : this.fhref || '#',
27325 cfg.cn.push(footer);
27332 onRender : function(ct,position){
27333 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27340 setHeadline: function (value)
27342 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27345 setFooter: function (value, href)
27347 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27350 this.el.select('a.small-box-footer',true).first().attr('href', href);
27355 setContent: function (value)
27357 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27360 initEvents: function()
27374 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27377 * @class Roo.bootstrap.dash.TabBox
27378 * @extends Roo.bootstrap.Component
27379 * Bootstrap TabBox class
27380 * @cfg {String} title Title of the TabBox
27381 * @cfg {String} icon Icon of the TabBox
27382 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27383 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27386 * Create a new TabBox
27387 * @param {Object} config The config object
27391 Roo.bootstrap.dash.TabBox = function(config){
27392 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27397 * When a pane is added
27398 * @param {Roo.bootstrap.dash.TabPane} pane
27402 * @event activatepane
27403 * When a pane is activated
27404 * @param {Roo.bootstrap.dash.TabPane} pane
27406 "activatepane" : true
27414 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27419 tabScrollable : false,
27421 getChildContainer : function()
27423 return this.el.select('.tab-content', true).first();
27426 getAutoCreate : function(){
27430 cls: 'pull-left header',
27438 cls: 'fa ' + this.icon
27444 cls: 'nav nav-tabs pull-right',
27450 if(this.tabScrollable){
27457 cls: 'nav nav-tabs pull-right',
27468 cls: 'nav-tabs-custom',
27473 cls: 'tab-content no-padding',
27481 initEvents : function()
27483 //Roo.log('add add pane handler');
27484 this.on('addpane', this.onAddPane, this);
27487 * Updates the box title
27488 * @param {String} html to set the title to.
27490 setTitle : function(value)
27492 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27494 onAddPane : function(pane)
27496 this.panes.push(pane);
27497 //Roo.log('addpane');
27499 // tabs are rendere left to right..
27500 if(!this.showtabs){
27504 var ctr = this.el.select('.nav-tabs', true).first();
27507 var existing = ctr.select('.nav-tab',true);
27508 var qty = existing.getCount();;
27511 var tab = ctr.createChild({
27513 cls : 'nav-tab' + (qty ? '' : ' active'),
27521 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27524 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27526 pane.el.addClass('active');
27531 onTabClick : function(ev,un,ob,pane)
27533 //Roo.log('tab - prev default');
27534 ev.preventDefault();
27537 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27538 pane.tab.addClass('active');
27539 //Roo.log(pane.title);
27540 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27541 // technically we should have a deactivate event.. but maybe add later.
27542 // and it should not de-activate the selected tab...
27543 this.fireEvent('activatepane', pane);
27544 pane.el.addClass('active');
27545 pane.fireEvent('activate');
27550 getActivePane : function()
27553 Roo.each(this.panes, function(p) {
27554 if(p.el.hasClass('active')){
27575 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27577 * @class Roo.bootstrap.TabPane
27578 * @extends Roo.bootstrap.Component
27579 * Bootstrap TabPane class
27580 * @cfg {Boolean} active (false | true) Default false
27581 * @cfg {String} title title of panel
27585 * Create a new TabPane
27586 * @param {Object} config The config object
27589 Roo.bootstrap.dash.TabPane = function(config){
27590 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27596 * When a pane is activated
27597 * @param {Roo.bootstrap.dash.TabPane} pane
27604 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27609 // the tabBox that this is attached to.
27612 getAutoCreate : function()
27620 cfg.cls += ' active';
27625 initEvents : function()
27627 //Roo.log('trigger add pane handler');
27628 this.parent().fireEvent('addpane', this)
27632 * Updates the tab title
27633 * @param {String} html to set the title to.
27635 setTitle: function(str)
27641 this.tab.select('a', true).first().dom.innerHTML = str;
27658 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27661 * @class Roo.bootstrap.menu.Menu
27662 * @extends Roo.bootstrap.Component
27663 * Bootstrap Menu class - container for Menu
27664 * @cfg {String} html Text of the menu
27665 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27666 * @cfg {String} icon Font awesome icon
27667 * @cfg {String} pos Menu align to (top | bottom) default bottom
27671 * Create a new Menu
27672 * @param {Object} config The config object
27676 Roo.bootstrap.menu.Menu = function(config){
27677 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27681 * @event beforeshow
27682 * Fires before this menu is displayed
27683 * @param {Roo.bootstrap.menu.Menu} this
27687 * @event beforehide
27688 * Fires before this menu is hidden
27689 * @param {Roo.bootstrap.menu.Menu} this
27694 * Fires after this menu is displayed
27695 * @param {Roo.bootstrap.menu.Menu} this
27700 * Fires after this menu is hidden
27701 * @param {Roo.bootstrap.menu.Menu} this
27706 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27707 * @param {Roo.bootstrap.menu.Menu} this
27708 * @param {Roo.EventObject} e
27715 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27719 weight : 'default',
27724 getChildContainer : function() {
27725 if(this.isSubMenu){
27729 return this.el.select('ul.dropdown-menu', true).first();
27732 getAutoCreate : function()
27737 cls : 'roo-menu-text',
27745 cls : 'fa ' + this.icon
27756 cls : 'dropdown-button btn btn-' + this.weight,
27761 cls : 'dropdown-toggle btn btn-' + this.weight,
27771 cls : 'dropdown-menu'
27777 if(this.pos == 'top'){
27778 cfg.cls += ' dropup';
27781 if(this.isSubMenu){
27784 cls : 'dropdown-menu'
27791 onRender : function(ct, position)
27793 this.isSubMenu = ct.hasClass('dropdown-submenu');
27795 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27798 initEvents : function()
27800 if(this.isSubMenu){
27804 this.hidden = true;
27806 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27807 this.triggerEl.on('click', this.onTriggerPress, this);
27809 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27810 this.buttonEl.on('click', this.onClick, this);
27816 if(this.isSubMenu){
27820 return this.el.select('ul.dropdown-menu', true).first();
27823 onClick : function(e)
27825 this.fireEvent("click", this, e);
27828 onTriggerPress : function(e)
27830 if (this.isVisible()) {
27837 isVisible : function(){
27838 return !this.hidden;
27843 this.fireEvent("beforeshow", this);
27845 this.hidden = false;
27846 this.el.addClass('open');
27848 Roo.get(document).on("mouseup", this.onMouseUp, this);
27850 this.fireEvent("show", this);
27857 this.fireEvent("beforehide", this);
27859 this.hidden = true;
27860 this.el.removeClass('open');
27862 Roo.get(document).un("mouseup", this.onMouseUp);
27864 this.fireEvent("hide", this);
27867 onMouseUp : function()
27881 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27884 * @class Roo.bootstrap.menu.Item
27885 * @extends Roo.bootstrap.Component
27886 * Bootstrap MenuItem class
27887 * @cfg {Boolean} submenu (true | false) default false
27888 * @cfg {String} html text of the item
27889 * @cfg {String} href the link
27890 * @cfg {Boolean} disable (true | false) default false
27891 * @cfg {Boolean} preventDefault (true | false) default true
27892 * @cfg {String} icon Font awesome icon
27893 * @cfg {String} pos Submenu align to (left | right) default right
27897 * Create a new Item
27898 * @param {Object} config The config object
27902 Roo.bootstrap.menu.Item = function(config){
27903 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27907 * Fires when the mouse is hovering over this menu
27908 * @param {Roo.bootstrap.menu.Item} this
27909 * @param {Roo.EventObject} e
27914 * Fires when the mouse exits this menu
27915 * @param {Roo.bootstrap.menu.Item} this
27916 * @param {Roo.EventObject} e
27922 * The raw click event for the entire grid.
27923 * @param {Roo.EventObject} e
27929 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27934 preventDefault: true,
27939 getAutoCreate : function()
27944 cls : 'roo-menu-item-text',
27952 cls : 'fa ' + this.icon
27961 href : this.href || '#',
27968 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27972 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27974 if(this.pos == 'left'){
27975 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27982 initEvents : function()
27984 this.el.on('mouseover', this.onMouseOver, this);
27985 this.el.on('mouseout', this.onMouseOut, this);
27987 this.el.select('a', true).first().on('click', this.onClick, this);
27991 onClick : function(e)
27993 if(this.preventDefault){
27994 e.preventDefault();
27997 this.fireEvent("click", this, e);
28000 onMouseOver : function(e)
28002 if(this.submenu && this.pos == 'left'){
28003 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28006 this.fireEvent("mouseover", this, e);
28009 onMouseOut : function(e)
28011 this.fireEvent("mouseout", this, e);
28023 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28026 * @class Roo.bootstrap.menu.Separator
28027 * @extends Roo.bootstrap.Component
28028 * Bootstrap Separator class
28031 * Create a new Separator
28032 * @param {Object} config The config object
28036 Roo.bootstrap.menu.Separator = function(config){
28037 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28040 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28042 getAutoCreate : function(){
28063 * @class Roo.bootstrap.Tooltip
28064 * Bootstrap Tooltip class
28065 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28066 * to determine which dom element triggers the tooltip.
28068 * It needs to add support for additional attributes like tooltip-position
28071 * Create a new Toolti
28072 * @param {Object} config The config object
28075 Roo.bootstrap.Tooltip = function(config){
28076 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28078 this.alignment = Roo.bootstrap.Tooltip.alignment;
28080 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28081 this.alignment = config.alignment;
28086 Roo.apply(Roo.bootstrap.Tooltip, {
28088 * @function init initialize tooltip monitoring.
28092 currentTip : false,
28093 currentRegion : false,
28099 Roo.get(document).on('mouseover', this.enter ,this);
28100 Roo.get(document).on('mouseout', this.leave, this);
28103 this.currentTip = new Roo.bootstrap.Tooltip();
28106 enter : function(ev)
28108 var dom = ev.getTarget();
28110 //Roo.log(['enter',dom]);
28111 var el = Roo.fly(dom);
28112 if (this.currentEl) {
28114 //Roo.log(this.currentEl);
28115 //Roo.log(this.currentEl.contains(dom));
28116 if (this.currentEl == el) {
28119 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28125 if (this.currentTip.el) {
28126 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28130 if(!el || el.dom == document){
28136 // you can not look for children, as if el is the body.. then everythign is the child..
28137 if (!el.attr('tooltip')) { //
28138 if (!el.select("[tooltip]").elements.length) {
28141 // is the mouse over this child...?
28142 bindEl = el.select("[tooltip]").first();
28143 var xy = ev.getXY();
28144 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28145 //Roo.log("not in region.");
28148 //Roo.log("child element over..");
28151 this.currentEl = bindEl;
28152 this.currentTip.bind(bindEl);
28153 this.currentRegion = Roo.lib.Region.getRegion(dom);
28154 this.currentTip.enter();
28157 leave : function(ev)
28159 var dom = ev.getTarget();
28160 //Roo.log(['leave',dom]);
28161 if (!this.currentEl) {
28166 if (dom != this.currentEl.dom) {
28169 var xy = ev.getXY();
28170 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28173 // only activate leave if mouse cursor is outside... bounding box..
28178 if (this.currentTip) {
28179 this.currentTip.leave();
28181 //Roo.log('clear currentEl');
28182 this.currentEl = false;
28187 'left' : ['r-l', [-2,0], 'right'],
28188 'right' : ['l-r', [2,0], 'left'],
28189 'bottom' : ['t-b', [0,2], 'top'],
28190 'top' : [ 'b-t', [0,-2], 'bottom']
28196 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28201 delay : null, // can be { show : 300 , hide: 500}
28205 hoverState : null, //???
28207 placement : 'bottom',
28211 getAutoCreate : function(){
28218 cls : 'tooltip-arrow'
28221 cls : 'tooltip-inner'
28228 bind : function(el)
28234 enter : function () {
28236 if (this.timeout != null) {
28237 clearTimeout(this.timeout);
28240 this.hoverState = 'in';
28241 //Roo.log("enter - show");
28242 if (!this.delay || !this.delay.show) {
28247 this.timeout = setTimeout(function () {
28248 if (_t.hoverState == 'in') {
28251 }, this.delay.show);
28255 clearTimeout(this.timeout);
28257 this.hoverState = 'out';
28258 if (!this.delay || !this.delay.hide) {
28264 this.timeout = setTimeout(function () {
28265 //Roo.log("leave - timeout");
28267 if (_t.hoverState == 'out') {
28269 Roo.bootstrap.Tooltip.currentEl = false;
28274 show : function (msg)
28277 this.render(document.body);
28280 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28282 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28284 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28286 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28288 var placement = typeof this.placement == 'function' ?
28289 this.placement.call(this, this.el, on_el) :
28292 var autoToken = /\s?auto?\s?/i;
28293 var autoPlace = autoToken.test(placement);
28295 placement = placement.replace(autoToken, '') || 'top';
28299 //this.el.setXY([0,0]);
28301 //this.el.dom.style.display='block';
28303 //this.el.appendTo(on_el);
28305 var p = this.getPosition();
28306 var box = this.el.getBox();
28312 var align = this.alignment[placement];
28314 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28316 if(placement == 'top' || placement == 'bottom'){
28318 placement = 'right';
28321 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28322 placement = 'left';
28325 var scroll = Roo.select('body', true).first().getScroll();
28327 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28331 align = this.alignment[placement];
28334 this.el.alignTo(this.bindEl, align[0],align[1]);
28335 //var arrow = this.el.select('.arrow',true).first();
28336 //arrow.set(align[2],
28338 this.el.addClass(placement);
28340 this.el.addClass('in fade');
28342 this.hoverState = null;
28344 if (this.el.hasClass('fade')) {
28355 //this.el.setXY([0,0]);
28356 this.el.removeClass('in');
28372 * @class Roo.bootstrap.LocationPicker
28373 * @extends Roo.bootstrap.Component
28374 * Bootstrap LocationPicker class
28375 * @cfg {Number} latitude Position when init default 0
28376 * @cfg {Number} longitude Position when init default 0
28377 * @cfg {Number} zoom default 15
28378 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28379 * @cfg {Boolean} mapTypeControl default false
28380 * @cfg {Boolean} disableDoubleClickZoom default false
28381 * @cfg {Boolean} scrollwheel default true
28382 * @cfg {Boolean} streetViewControl default false
28383 * @cfg {Number} radius default 0
28384 * @cfg {String} locationName
28385 * @cfg {Boolean} draggable default true
28386 * @cfg {Boolean} enableAutocomplete default false
28387 * @cfg {Boolean} enableReverseGeocode default true
28388 * @cfg {String} markerTitle
28391 * Create a new LocationPicker
28392 * @param {Object} config The config object
28396 Roo.bootstrap.LocationPicker = function(config){
28398 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28403 * Fires when the picker initialized.
28404 * @param {Roo.bootstrap.LocationPicker} this
28405 * @param {Google Location} location
28409 * @event positionchanged
28410 * Fires when the picker position changed.
28411 * @param {Roo.bootstrap.LocationPicker} this
28412 * @param {Google Location} location
28414 positionchanged : true,
28417 * Fires when the map resize.
28418 * @param {Roo.bootstrap.LocationPicker} this
28423 * Fires when the map show.
28424 * @param {Roo.bootstrap.LocationPicker} this
28429 * Fires when the map hide.
28430 * @param {Roo.bootstrap.LocationPicker} this
28435 * Fires when click the map.
28436 * @param {Roo.bootstrap.LocationPicker} this
28437 * @param {Map event} e
28441 * @event mapRightClick
28442 * Fires when right click the map.
28443 * @param {Roo.bootstrap.LocationPicker} this
28444 * @param {Map event} e
28446 mapRightClick : true,
28448 * @event markerClick
28449 * Fires when click the marker.
28450 * @param {Roo.bootstrap.LocationPicker} this
28451 * @param {Map event} e
28453 markerClick : true,
28455 * @event markerRightClick
28456 * Fires when right click the marker.
28457 * @param {Roo.bootstrap.LocationPicker} this
28458 * @param {Map event} e
28460 markerRightClick : true,
28462 * @event OverlayViewDraw
28463 * Fires when OverlayView Draw
28464 * @param {Roo.bootstrap.LocationPicker} this
28466 OverlayViewDraw : true,
28468 * @event OverlayViewOnAdd
28469 * Fires when OverlayView Draw
28470 * @param {Roo.bootstrap.LocationPicker} this
28472 OverlayViewOnAdd : true,
28474 * @event OverlayViewOnRemove
28475 * Fires when OverlayView Draw
28476 * @param {Roo.bootstrap.LocationPicker} this
28478 OverlayViewOnRemove : true,
28480 * @event OverlayViewShow
28481 * Fires when OverlayView Draw
28482 * @param {Roo.bootstrap.LocationPicker} this
28483 * @param {Pixel} cpx
28485 OverlayViewShow : true,
28487 * @event OverlayViewHide
28488 * Fires when OverlayView Draw
28489 * @param {Roo.bootstrap.LocationPicker} this
28491 OverlayViewHide : true,
28493 * @event loadexception
28494 * Fires when load google lib failed.
28495 * @param {Roo.bootstrap.LocationPicker} this
28497 loadexception : true
28502 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28504 gMapContext: false,
28510 mapTypeControl: false,
28511 disableDoubleClickZoom: false,
28513 streetViewControl: false,
28517 enableAutocomplete: false,
28518 enableReverseGeocode: true,
28521 getAutoCreate: function()
28526 cls: 'roo-location-picker'
28532 initEvents: function(ct, position)
28534 if(!this.el.getWidth() || this.isApplied()){
28538 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28543 initial: function()
28545 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28546 this.fireEvent('loadexception', this);
28550 if(!this.mapTypeId){
28551 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28554 this.gMapContext = this.GMapContext();
28556 this.initOverlayView();
28558 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28562 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28563 _this.setPosition(_this.gMapContext.marker.position);
28566 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28567 _this.fireEvent('mapClick', this, event);
28571 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28572 _this.fireEvent('mapRightClick', this, event);
28576 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28577 _this.fireEvent('markerClick', this, event);
28581 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28582 _this.fireEvent('markerRightClick', this, event);
28586 this.setPosition(this.gMapContext.location);
28588 this.fireEvent('initial', this, this.gMapContext.location);
28591 initOverlayView: function()
28595 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28599 _this.fireEvent('OverlayViewDraw', _this);
28604 _this.fireEvent('OverlayViewOnAdd', _this);
28607 onRemove: function()
28609 _this.fireEvent('OverlayViewOnRemove', _this);
28612 show: function(cpx)
28614 _this.fireEvent('OverlayViewShow', _this, cpx);
28619 _this.fireEvent('OverlayViewHide', _this);
28625 fromLatLngToContainerPixel: function(event)
28627 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28630 isApplied: function()
28632 return this.getGmapContext() == false ? false : true;
28635 getGmapContext: function()
28637 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28640 GMapContext: function()
28642 var position = new google.maps.LatLng(this.latitude, this.longitude);
28644 var _map = new google.maps.Map(this.el.dom, {
28647 mapTypeId: this.mapTypeId,
28648 mapTypeControl: this.mapTypeControl,
28649 disableDoubleClickZoom: this.disableDoubleClickZoom,
28650 scrollwheel: this.scrollwheel,
28651 streetViewControl: this.streetViewControl,
28652 locationName: this.locationName,
28653 draggable: this.draggable,
28654 enableAutocomplete: this.enableAutocomplete,
28655 enableReverseGeocode: this.enableReverseGeocode
28658 var _marker = new google.maps.Marker({
28659 position: position,
28661 title: this.markerTitle,
28662 draggable: this.draggable
28669 location: position,
28670 radius: this.radius,
28671 locationName: this.locationName,
28672 addressComponents: {
28673 formatted_address: null,
28674 addressLine1: null,
28675 addressLine2: null,
28677 streetNumber: null,
28681 stateOrProvince: null
28684 domContainer: this.el.dom,
28685 geodecoder: new google.maps.Geocoder()
28689 drawCircle: function(center, radius, options)
28691 if (this.gMapContext.circle != null) {
28692 this.gMapContext.circle.setMap(null);
28696 options = Roo.apply({}, options, {
28697 strokeColor: "#0000FF",
28698 strokeOpacity: .35,
28700 fillColor: "#0000FF",
28704 options.map = this.gMapContext.map;
28705 options.radius = radius;
28706 options.center = center;
28707 this.gMapContext.circle = new google.maps.Circle(options);
28708 return this.gMapContext.circle;
28714 setPosition: function(location)
28716 this.gMapContext.location = location;
28717 this.gMapContext.marker.setPosition(location);
28718 this.gMapContext.map.panTo(location);
28719 this.drawCircle(location, this.gMapContext.radius, {});
28723 if (this.gMapContext.settings.enableReverseGeocode) {
28724 this.gMapContext.geodecoder.geocode({
28725 latLng: this.gMapContext.location
28726 }, function(results, status) {
28728 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28729 _this.gMapContext.locationName = results[0].formatted_address;
28730 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28732 _this.fireEvent('positionchanged', this, location);
28739 this.fireEvent('positionchanged', this, location);
28744 google.maps.event.trigger(this.gMapContext.map, "resize");
28746 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28748 this.fireEvent('resize', this);
28751 setPositionByLatLng: function(latitude, longitude)
28753 this.setPosition(new google.maps.LatLng(latitude, longitude));
28756 getCurrentPosition: function()
28759 latitude: this.gMapContext.location.lat(),
28760 longitude: this.gMapContext.location.lng()
28764 getAddressName: function()
28766 return this.gMapContext.locationName;
28769 getAddressComponents: function()
28771 return this.gMapContext.addressComponents;
28774 address_component_from_google_geocode: function(address_components)
28778 for (var i = 0; i < address_components.length; i++) {
28779 var component = address_components[i];
28780 if (component.types.indexOf("postal_code") >= 0) {
28781 result.postalCode = component.short_name;
28782 } else if (component.types.indexOf("street_number") >= 0) {
28783 result.streetNumber = component.short_name;
28784 } else if (component.types.indexOf("route") >= 0) {
28785 result.streetName = component.short_name;
28786 } else if (component.types.indexOf("neighborhood") >= 0) {
28787 result.city = component.short_name;
28788 } else if (component.types.indexOf("locality") >= 0) {
28789 result.city = component.short_name;
28790 } else if (component.types.indexOf("sublocality") >= 0) {
28791 result.district = component.short_name;
28792 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28793 result.stateOrProvince = component.short_name;
28794 } else if (component.types.indexOf("country") >= 0) {
28795 result.country = component.short_name;
28799 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28800 result.addressLine2 = "";
28804 setZoomLevel: function(zoom)
28806 this.gMapContext.map.setZoom(zoom);
28819 this.fireEvent('show', this);
28830 this.fireEvent('hide', this);
28835 Roo.apply(Roo.bootstrap.LocationPicker, {
28837 OverlayView : function(map, options)
28839 options = options || {};
28846 * @class Roo.bootstrap.Alert
28847 * @extends Roo.bootstrap.Component
28848 * Bootstrap Alert class - shows an alert area box
28850 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28851 Enter a valid email address
28854 * @cfg {String} title The title of alert
28855 * @cfg {String} html The content of alert
28856 * @cfg {String} weight ( success | info | warning | danger )
28857 * @cfg {String} faicon font-awesomeicon
28860 * Create a new alert
28861 * @param {Object} config The config object
28865 Roo.bootstrap.Alert = function(config){
28866 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28870 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28877 getAutoCreate : function()
28886 cls : 'roo-alert-icon'
28891 cls : 'roo-alert-title',
28896 cls : 'roo-alert-text',
28903 cfg.cn[0].cls += ' fa ' + this.faicon;
28907 cfg.cls += ' alert-' + this.weight;
28913 initEvents: function()
28915 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28918 setTitle : function(str)
28920 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28923 setText : function(str)
28925 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28928 setWeight : function(weight)
28931 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28934 this.weight = weight;
28936 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28939 setIcon : function(icon)
28942 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28945 this.faicon = icon;
28947 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28968 * @class Roo.bootstrap.UploadCropbox
28969 * @extends Roo.bootstrap.Component
28970 * Bootstrap UploadCropbox class
28971 * @cfg {String} emptyText show when image has been loaded
28972 * @cfg {String} rotateNotify show when image too small to rotate
28973 * @cfg {Number} errorTimeout default 3000
28974 * @cfg {Number} minWidth default 300
28975 * @cfg {Number} minHeight default 300
28976 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28977 * @cfg {Boolean} isDocument (true|false) default false
28978 * @cfg {String} url action url
28979 * @cfg {String} paramName default 'imageUpload'
28980 * @cfg {String} method default POST
28981 * @cfg {Boolean} loadMask (true|false) default true
28982 * @cfg {Boolean} loadingText default 'Loading...'
28985 * Create a new UploadCropbox
28986 * @param {Object} config The config object
28989 Roo.bootstrap.UploadCropbox = function(config){
28990 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28994 * @event beforeselectfile
28995 * Fire before select file
28996 * @param {Roo.bootstrap.UploadCropbox} this
28998 "beforeselectfile" : true,
29001 * Fire after initEvent
29002 * @param {Roo.bootstrap.UploadCropbox} this
29007 * Fire after initEvent
29008 * @param {Roo.bootstrap.UploadCropbox} this
29009 * @param {String} data
29014 * Fire when preparing the file data
29015 * @param {Roo.bootstrap.UploadCropbox} this
29016 * @param {Object} file
29021 * Fire when get exception
29022 * @param {Roo.bootstrap.UploadCropbox} this
29023 * @param {XMLHttpRequest} xhr
29025 "exception" : true,
29027 * @event beforeloadcanvas
29028 * Fire before load the canvas
29029 * @param {Roo.bootstrap.UploadCropbox} this
29030 * @param {String} src
29032 "beforeloadcanvas" : true,
29035 * Fire when trash image
29036 * @param {Roo.bootstrap.UploadCropbox} this
29041 * Fire when download the image
29042 * @param {Roo.bootstrap.UploadCropbox} this
29046 * @event footerbuttonclick
29047 * Fire when footerbuttonclick
29048 * @param {Roo.bootstrap.UploadCropbox} this
29049 * @param {String} type
29051 "footerbuttonclick" : true,
29055 * @param {Roo.bootstrap.UploadCropbox} this
29060 * Fire when rotate the image
29061 * @param {Roo.bootstrap.UploadCropbox} this
29062 * @param {String} pos
29067 * Fire when inspect the file
29068 * @param {Roo.bootstrap.UploadCropbox} this
29069 * @param {Object} file
29074 * Fire when xhr upload the file
29075 * @param {Roo.bootstrap.UploadCropbox} this
29076 * @param {Object} data
29081 * Fire when arrange the file data
29082 * @param {Roo.bootstrap.UploadCropbox} this
29083 * @param {Object} formData
29088 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29091 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29093 emptyText : 'Click to upload image',
29094 rotateNotify : 'Image is too small to rotate',
29095 errorTimeout : 3000,
29109 cropType : 'image/jpeg',
29111 canvasLoaded : false,
29112 isDocument : false,
29114 paramName : 'imageUpload',
29116 loadingText : 'Loading...',
29119 getAutoCreate : function()
29123 cls : 'roo-upload-cropbox',
29127 cls : 'roo-upload-cropbox-selector',
29132 cls : 'roo-upload-cropbox-body',
29133 style : 'cursor:pointer',
29137 cls : 'roo-upload-cropbox-preview'
29141 cls : 'roo-upload-cropbox-thumb'
29145 cls : 'roo-upload-cropbox-empty-notify',
29146 html : this.emptyText
29150 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29151 html : this.rotateNotify
29157 cls : 'roo-upload-cropbox-footer',
29160 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29170 onRender : function(ct, position)
29172 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29174 if (this.buttons.length) {
29176 Roo.each(this.buttons, function(bb) {
29178 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29180 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29186 this.maskEl = this.el;
29190 initEvents : function()
29192 this.urlAPI = (window.createObjectURL && window) ||
29193 (window.URL && URL.revokeObjectURL && URL) ||
29194 (window.webkitURL && webkitURL);
29196 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29197 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29199 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29200 this.selectorEl.hide();
29202 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29203 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29205 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29206 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29207 this.thumbEl.hide();
29209 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29210 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29212 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29213 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29214 this.errorEl.hide();
29216 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29217 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29218 this.footerEl.hide();
29220 this.setThumbBoxSize();
29226 this.fireEvent('initial', this);
29233 window.addEventListener("resize", function() { _this.resize(); } );
29235 this.bodyEl.on('click', this.beforeSelectFile, this);
29238 this.bodyEl.on('touchstart', this.onTouchStart, this);
29239 this.bodyEl.on('touchmove', this.onTouchMove, this);
29240 this.bodyEl.on('touchend', this.onTouchEnd, this);
29244 this.bodyEl.on('mousedown', this.onMouseDown, this);
29245 this.bodyEl.on('mousemove', this.onMouseMove, this);
29246 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29247 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29248 Roo.get(document).on('mouseup', this.onMouseUp, this);
29251 this.selectorEl.on('change', this.onFileSelected, this);
29257 this.baseScale = 1;
29259 this.baseRotate = 1;
29260 this.dragable = false;
29261 this.pinching = false;
29264 this.cropData = false;
29265 this.notifyEl.dom.innerHTML = this.emptyText;
29267 this.selectorEl.dom.value = '';
29271 resize : function()
29273 if(this.fireEvent('resize', this) != false){
29274 this.setThumbBoxPosition();
29275 this.setCanvasPosition();
29279 onFooterButtonClick : function(e, el, o, type)
29282 case 'rotate-left' :
29283 this.onRotateLeft(e);
29285 case 'rotate-right' :
29286 this.onRotateRight(e);
29289 this.beforeSelectFile(e);
29304 this.fireEvent('footerbuttonclick', this, type);
29307 beforeSelectFile : function(e)
29309 e.preventDefault();
29311 if(this.fireEvent('beforeselectfile', this) != false){
29312 this.selectorEl.dom.click();
29316 onFileSelected : function(e)
29318 e.preventDefault();
29320 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29324 var file = this.selectorEl.dom.files[0];
29326 if(this.fireEvent('inspect', this, file) != false){
29327 this.prepare(file);
29332 trash : function(e)
29334 this.fireEvent('trash', this);
29337 download : function(e)
29339 this.fireEvent('download', this);
29342 loadCanvas : function(src)
29344 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29348 this.imageEl = document.createElement('img');
29352 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29354 this.imageEl.src = src;
29358 onLoadCanvas : function()
29360 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29361 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29363 this.bodyEl.un('click', this.beforeSelectFile, this);
29365 this.notifyEl.hide();
29366 this.thumbEl.show();
29367 this.footerEl.show();
29369 this.baseRotateLevel();
29371 if(this.isDocument){
29372 this.setThumbBoxSize();
29375 this.setThumbBoxPosition();
29377 this.baseScaleLevel();
29383 this.canvasLoaded = true;
29386 this.maskEl.unmask();
29391 setCanvasPosition : function()
29393 if(!this.canvasEl){
29397 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29398 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29400 this.previewEl.setLeft(pw);
29401 this.previewEl.setTop(ph);
29405 onMouseDown : function(e)
29409 this.dragable = true;
29410 this.pinching = false;
29412 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29413 this.dragable = false;
29417 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29418 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29422 onMouseMove : function(e)
29426 if(!this.canvasLoaded){
29430 if (!this.dragable){
29434 var minX = Math.ceil(this.thumbEl.getLeft(true));
29435 var minY = Math.ceil(this.thumbEl.getTop(true));
29437 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29438 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29440 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29441 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29443 x = x - this.mouseX;
29444 y = y - this.mouseY;
29446 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29447 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29449 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29450 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29452 this.previewEl.setLeft(bgX);
29453 this.previewEl.setTop(bgY);
29455 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29456 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29459 onMouseUp : function(e)
29463 this.dragable = false;
29466 onMouseWheel : function(e)
29470 this.startScale = this.scale;
29472 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29474 if(!this.zoomable()){
29475 this.scale = this.startScale;
29484 zoomable : function()
29486 var minScale = this.thumbEl.getWidth() / this.minWidth;
29488 if(this.minWidth < this.minHeight){
29489 minScale = this.thumbEl.getHeight() / this.minHeight;
29492 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29493 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29497 (this.rotate == 0 || this.rotate == 180) &&
29499 width > this.imageEl.OriginWidth ||
29500 height > this.imageEl.OriginHeight ||
29501 (width < this.minWidth && height < this.minHeight)
29509 (this.rotate == 90 || this.rotate == 270) &&
29511 width > this.imageEl.OriginWidth ||
29512 height > this.imageEl.OriginHeight ||
29513 (width < this.minHeight && height < this.minWidth)
29520 !this.isDocument &&
29521 (this.rotate == 0 || this.rotate == 180) &&
29523 width < this.minWidth ||
29524 width > this.imageEl.OriginWidth ||
29525 height < this.minHeight ||
29526 height > this.imageEl.OriginHeight
29533 !this.isDocument &&
29534 (this.rotate == 90 || this.rotate == 270) &&
29536 width < this.minHeight ||
29537 width > this.imageEl.OriginWidth ||
29538 height < this.minWidth ||
29539 height > this.imageEl.OriginHeight
29549 onRotateLeft : function(e)
29551 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29553 var minScale = this.thumbEl.getWidth() / this.minWidth;
29555 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29556 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29558 this.startScale = this.scale;
29560 while (this.getScaleLevel() < minScale){
29562 this.scale = this.scale + 1;
29564 if(!this.zoomable()){
29569 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29570 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29575 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29582 this.scale = this.startScale;
29584 this.onRotateFail();
29589 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29591 if(this.isDocument){
29592 this.setThumbBoxSize();
29593 this.setThumbBoxPosition();
29594 this.setCanvasPosition();
29599 this.fireEvent('rotate', this, 'left');
29603 onRotateRight : function(e)
29605 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29607 var minScale = this.thumbEl.getWidth() / this.minWidth;
29609 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29610 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29612 this.startScale = this.scale;
29614 while (this.getScaleLevel() < minScale){
29616 this.scale = this.scale + 1;
29618 if(!this.zoomable()){
29623 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29624 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29629 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29636 this.scale = this.startScale;
29638 this.onRotateFail();
29643 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29645 if(this.isDocument){
29646 this.setThumbBoxSize();
29647 this.setThumbBoxPosition();
29648 this.setCanvasPosition();
29653 this.fireEvent('rotate', this, 'right');
29656 onRotateFail : function()
29658 this.errorEl.show(true);
29662 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29667 this.previewEl.dom.innerHTML = '';
29669 var canvasEl = document.createElement("canvas");
29671 var contextEl = canvasEl.getContext("2d");
29673 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29674 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29675 var center = this.imageEl.OriginWidth / 2;
29677 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29678 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29679 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29680 center = this.imageEl.OriginHeight / 2;
29683 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29685 contextEl.translate(center, center);
29686 contextEl.rotate(this.rotate * Math.PI / 180);
29688 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29690 this.canvasEl = document.createElement("canvas");
29692 this.contextEl = this.canvasEl.getContext("2d");
29694 switch (this.rotate) {
29697 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29698 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29700 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29705 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29706 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29708 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29709 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);
29713 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29718 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29719 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29721 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29722 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);
29726 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);
29731 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29732 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29734 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29735 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29739 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);
29746 this.previewEl.appendChild(this.canvasEl);
29748 this.setCanvasPosition();
29753 if(!this.canvasLoaded){
29757 var imageCanvas = document.createElement("canvas");
29759 var imageContext = imageCanvas.getContext("2d");
29761 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29762 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29764 var center = imageCanvas.width / 2;
29766 imageContext.translate(center, center);
29768 imageContext.rotate(this.rotate * Math.PI / 180);
29770 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29772 var canvas = document.createElement("canvas");
29774 var context = canvas.getContext("2d");
29776 canvas.width = this.minWidth;
29777 canvas.height = this.minHeight;
29779 switch (this.rotate) {
29782 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29783 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29785 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29786 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29788 var targetWidth = this.minWidth - 2 * x;
29789 var targetHeight = this.minHeight - 2 * y;
29793 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29794 scale = targetWidth / width;
29797 if(x > 0 && y == 0){
29798 scale = targetHeight / height;
29801 if(x > 0 && y > 0){
29802 scale = targetWidth / width;
29804 if(width < height){
29805 scale = targetHeight / height;
29809 context.scale(scale, scale);
29811 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29812 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29814 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29815 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29817 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29822 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29823 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29825 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29826 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29828 var targetWidth = this.minWidth - 2 * x;
29829 var targetHeight = this.minHeight - 2 * y;
29833 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29834 scale = targetWidth / width;
29837 if(x > 0 && y == 0){
29838 scale = targetHeight / height;
29841 if(x > 0 && y > 0){
29842 scale = targetWidth / width;
29844 if(width < height){
29845 scale = targetHeight / height;
29849 context.scale(scale, scale);
29851 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29852 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29854 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29855 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29857 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29859 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29864 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29865 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29867 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29868 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29870 var targetWidth = this.minWidth - 2 * x;
29871 var targetHeight = this.minHeight - 2 * y;
29875 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29876 scale = targetWidth / width;
29879 if(x > 0 && y == 0){
29880 scale = targetHeight / height;
29883 if(x > 0 && y > 0){
29884 scale = targetWidth / width;
29886 if(width < height){
29887 scale = targetHeight / height;
29891 context.scale(scale, scale);
29893 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29894 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29896 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29897 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29899 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29900 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29902 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29907 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29908 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29910 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29911 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29913 var targetWidth = this.minWidth - 2 * x;
29914 var targetHeight = this.minHeight - 2 * y;
29918 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29919 scale = targetWidth / width;
29922 if(x > 0 && y == 0){
29923 scale = targetHeight / height;
29926 if(x > 0 && y > 0){
29927 scale = targetWidth / width;
29929 if(width < height){
29930 scale = targetHeight / height;
29934 context.scale(scale, scale);
29936 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29937 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29939 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29940 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29942 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29944 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29951 this.cropData = canvas.toDataURL(this.cropType);
29953 if(this.fireEvent('crop', this, this.cropData) !== false){
29954 this.process(this.file, this.cropData);
29961 setThumbBoxSize : function()
29965 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29966 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29967 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29969 this.minWidth = width;
29970 this.minHeight = height;
29972 if(this.rotate == 90 || this.rotate == 270){
29973 this.minWidth = height;
29974 this.minHeight = width;
29979 width = Math.ceil(this.minWidth * height / this.minHeight);
29981 if(this.minWidth > this.minHeight){
29983 height = Math.ceil(this.minHeight * width / this.minWidth);
29986 this.thumbEl.setStyle({
29987 width : width + 'px',
29988 height : height + 'px'
29995 setThumbBoxPosition : function()
29997 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29998 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30000 this.thumbEl.setLeft(x);
30001 this.thumbEl.setTop(y);
30005 baseRotateLevel : function()
30007 this.baseRotate = 1;
30010 typeof(this.exif) != 'undefined' &&
30011 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30012 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30014 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30017 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30021 baseScaleLevel : function()
30025 if(this.isDocument){
30027 if(this.baseRotate == 6 || this.baseRotate == 8){
30029 height = this.thumbEl.getHeight();
30030 this.baseScale = height / this.imageEl.OriginWidth;
30032 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30033 width = this.thumbEl.getWidth();
30034 this.baseScale = width / this.imageEl.OriginHeight;
30040 height = this.thumbEl.getHeight();
30041 this.baseScale = height / this.imageEl.OriginHeight;
30043 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30044 width = this.thumbEl.getWidth();
30045 this.baseScale = width / this.imageEl.OriginWidth;
30051 if(this.baseRotate == 6 || this.baseRotate == 8){
30053 width = this.thumbEl.getHeight();
30054 this.baseScale = width / this.imageEl.OriginHeight;
30056 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30057 height = this.thumbEl.getWidth();
30058 this.baseScale = height / this.imageEl.OriginHeight;
30061 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30062 height = this.thumbEl.getWidth();
30063 this.baseScale = height / this.imageEl.OriginHeight;
30065 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30066 width = this.thumbEl.getHeight();
30067 this.baseScale = width / this.imageEl.OriginWidth;
30074 width = this.thumbEl.getWidth();
30075 this.baseScale = width / this.imageEl.OriginWidth;
30077 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30078 height = this.thumbEl.getHeight();
30079 this.baseScale = height / this.imageEl.OriginHeight;
30082 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30084 height = this.thumbEl.getHeight();
30085 this.baseScale = height / this.imageEl.OriginHeight;
30087 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30088 width = this.thumbEl.getWidth();
30089 this.baseScale = width / this.imageEl.OriginWidth;
30097 getScaleLevel : function()
30099 return this.baseScale * Math.pow(1.1, this.scale);
30102 onTouchStart : function(e)
30104 if(!this.canvasLoaded){
30105 this.beforeSelectFile(e);
30109 var touches = e.browserEvent.touches;
30115 if(touches.length == 1){
30116 this.onMouseDown(e);
30120 if(touches.length != 2){
30126 for(var i = 0, finger; finger = touches[i]; i++){
30127 coords.push(finger.pageX, finger.pageY);
30130 var x = Math.pow(coords[0] - coords[2], 2);
30131 var y = Math.pow(coords[1] - coords[3], 2);
30133 this.startDistance = Math.sqrt(x + y);
30135 this.startScale = this.scale;
30137 this.pinching = true;
30138 this.dragable = false;
30142 onTouchMove : function(e)
30144 if(!this.pinching && !this.dragable){
30148 var touches = e.browserEvent.touches;
30155 this.onMouseMove(e);
30161 for(var i = 0, finger; finger = touches[i]; i++){
30162 coords.push(finger.pageX, finger.pageY);
30165 var x = Math.pow(coords[0] - coords[2], 2);
30166 var y = Math.pow(coords[1] - coords[3], 2);
30168 this.endDistance = Math.sqrt(x + y);
30170 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30172 if(!this.zoomable()){
30173 this.scale = this.startScale;
30181 onTouchEnd : function(e)
30183 this.pinching = false;
30184 this.dragable = false;
30188 process : function(file, crop)
30191 this.maskEl.mask(this.loadingText);
30194 this.xhr = new XMLHttpRequest();
30196 file.xhr = this.xhr;
30198 this.xhr.open(this.method, this.url, true);
30201 "Accept": "application/json",
30202 "Cache-Control": "no-cache",
30203 "X-Requested-With": "XMLHttpRequest"
30206 for (var headerName in headers) {
30207 var headerValue = headers[headerName];
30209 this.xhr.setRequestHeader(headerName, headerValue);
30215 this.xhr.onload = function()
30217 _this.xhrOnLoad(_this.xhr);
30220 this.xhr.onerror = function()
30222 _this.xhrOnError(_this.xhr);
30225 var formData = new FormData();
30227 formData.append('returnHTML', 'NO');
30230 formData.append('crop', crop);
30233 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30234 formData.append(this.paramName, file, file.name);
30237 if(typeof(file.filename) != 'undefined'){
30238 formData.append('filename', file.filename);
30241 if(typeof(file.mimetype) != 'undefined'){
30242 formData.append('mimetype', file.mimetype);
30245 if(this.fireEvent('arrange', this, formData) != false){
30246 this.xhr.send(formData);
30250 xhrOnLoad : function(xhr)
30253 this.maskEl.unmask();
30256 if (xhr.readyState !== 4) {
30257 this.fireEvent('exception', this, xhr);
30261 var response = Roo.decode(xhr.responseText);
30263 if(!response.success){
30264 this.fireEvent('exception', this, xhr);
30268 var response = Roo.decode(xhr.responseText);
30270 this.fireEvent('upload', this, response);
30274 xhrOnError : function()
30277 this.maskEl.unmask();
30280 Roo.log('xhr on error');
30282 var response = Roo.decode(xhr.responseText);
30288 prepare : function(file)
30291 this.maskEl.mask(this.loadingText);
30297 if(typeof(file) === 'string'){
30298 this.loadCanvas(file);
30302 if(!file || !this.urlAPI){
30307 this.cropType = file.type;
30311 if(this.fireEvent('prepare', this, this.file) != false){
30313 var reader = new FileReader();
30315 reader.onload = function (e) {
30316 if (e.target.error) {
30317 Roo.log(e.target.error);
30321 var buffer = e.target.result,
30322 dataView = new DataView(buffer),
30324 maxOffset = dataView.byteLength - 4,
30328 if (dataView.getUint16(0) === 0xffd8) {
30329 while (offset < maxOffset) {
30330 markerBytes = dataView.getUint16(offset);
30332 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30333 markerLength = dataView.getUint16(offset + 2) + 2;
30334 if (offset + markerLength > dataView.byteLength) {
30335 Roo.log('Invalid meta data: Invalid segment size.');
30339 if(markerBytes == 0xffe1){
30340 _this.parseExifData(
30347 offset += markerLength;
30357 var url = _this.urlAPI.createObjectURL(_this.file);
30359 _this.loadCanvas(url);
30364 reader.readAsArrayBuffer(this.file);
30370 parseExifData : function(dataView, offset, length)
30372 var tiffOffset = offset + 10,
30376 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30377 // No Exif data, might be XMP data instead
30381 // Check for the ASCII code for "Exif" (0x45786966):
30382 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30383 // No Exif data, might be XMP data instead
30386 if (tiffOffset + 8 > dataView.byteLength) {
30387 Roo.log('Invalid Exif data: Invalid segment size.');
30390 // Check for the two null bytes:
30391 if (dataView.getUint16(offset + 8) !== 0x0000) {
30392 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30395 // Check the byte alignment:
30396 switch (dataView.getUint16(tiffOffset)) {
30398 littleEndian = true;
30401 littleEndian = false;
30404 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30407 // Check for the TIFF tag marker (0x002A):
30408 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30409 Roo.log('Invalid Exif data: Missing TIFF marker.');
30412 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30413 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30415 this.parseExifTags(
30418 tiffOffset + dirOffset,
30423 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30428 if (dirOffset + 6 > dataView.byteLength) {
30429 Roo.log('Invalid Exif data: Invalid directory offset.');
30432 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30433 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30434 if (dirEndOffset + 4 > dataView.byteLength) {
30435 Roo.log('Invalid Exif data: Invalid directory size.');
30438 for (i = 0; i < tagsNumber; i += 1) {
30442 dirOffset + 2 + 12 * i, // tag offset
30446 // Return the offset to the next directory:
30447 return dataView.getUint32(dirEndOffset, littleEndian);
30450 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30452 var tag = dataView.getUint16(offset, littleEndian);
30454 this.exif[tag] = this.getExifValue(
30458 dataView.getUint16(offset + 2, littleEndian), // tag type
30459 dataView.getUint32(offset + 4, littleEndian), // tag length
30464 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30466 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30475 Roo.log('Invalid Exif data: Invalid tag type.');
30479 tagSize = tagType.size * length;
30480 // Determine if the value is contained in the dataOffset bytes,
30481 // or if the value at the dataOffset is a pointer to the actual data:
30482 dataOffset = tagSize > 4 ?
30483 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30484 if (dataOffset + tagSize > dataView.byteLength) {
30485 Roo.log('Invalid Exif data: Invalid data offset.');
30488 if (length === 1) {
30489 return tagType.getValue(dataView, dataOffset, littleEndian);
30492 for (i = 0; i < length; i += 1) {
30493 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30496 if (tagType.ascii) {
30498 // Concatenate the chars:
30499 for (i = 0; i < values.length; i += 1) {
30501 // Ignore the terminating NULL byte(s):
30502 if (c === '\u0000') {
30514 Roo.apply(Roo.bootstrap.UploadCropbox, {
30516 'Orientation': 0x0112
30520 1: 0, //'top-left',
30522 3: 180, //'bottom-right',
30523 // 4: 'bottom-left',
30525 6: 90, //'right-top',
30526 // 7: 'right-bottom',
30527 8: 270 //'left-bottom'
30531 // byte, 8-bit unsigned int:
30533 getValue: function (dataView, dataOffset) {
30534 return dataView.getUint8(dataOffset);
30538 // ascii, 8-bit byte:
30540 getValue: function (dataView, dataOffset) {
30541 return String.fromCharCode(dataView.getUint8(dataOffset));
30546 // short, 16 bit int:
30548 getValue: function (dataView, dataOffset, littleEndian) {
30549 return dataView.getUint16(dataOffset, littleEndian);
30553 // long, 32 bit int:
30555 getValue: function (dataView, dataOffset, littleEndian) {
30556 return dataView.getUint32(dataOffset, littleEndian);
30560 // rational = two long values, first is numerator, second is denominator:
30562 getValue: function (dataView, dataOffset, littleEndian) {
30563 return dataView.getUint32(dataOffset, littleEndian) /
30564 dataView.getUint32(dataOffset + 4, littleEndian);
30568 // slong, 32 bit signed int:
30570 getValue: function (dataView, dataOffset, littleEndian) {
30571 return dataView.getInt32(dataOffset, littleEndian);
30575 // srational, two slongs, first is numerator, second is denominator:
30577 getValue: function (dataView, dataOffset, littleEndian) {
30578 return dataView.getInt32(dataOffset, littleEndian) /
30579 dataView.getInt32(dataOffset + 4, littleEndian);
30589 cls : 'btn-group roo-upload-cropbox-rotate-left',
30590 action : 'rotate-left',
30594 cls : 'btn btn-default',
30595 html : '<i class="fa fa-undo"></i>'
30601 cls : 'btn-group roo-upload-cropbox-picture',
30602 action : 'picture',
30606 cls : 'btn btn-default',
30607 html : '<i class="fa fa-picture-o"></i>'
30613 cls : 'btn-group roo-upload-cropbox-rotate-right',
30614 action : 'rotate-right',
30618 cls : 'btn btn-default',
30619 html : '<i class="fa fa-repeat"></i>'
30627 cls : 'btn-group roo-upload-cropbox-rotate-left',
30628 action : 'rotate-left',
30632 cls : 'btn btn-default',
30633 html : '<i class="fa fa-undo"></i>'
30639 cls : 'btn-group roo-upload-cropbox-download',
30640 action : 'download',
30644 cls : 'btn btn-default',
30645 html : '<i class="fa fa-download"></i>'
30651 cls : 'btn-group roo-upload-cropbox-crop',
30656 cls : 'btn btn-default',
30657 html : '<i class="fa fa-crop"></i>'
30663 cls : 'btn-group roo-upload-cropbox-trash',
30668 cls : 'btn btn-default',
30669 html : '<i class="fa fa-trash"></i>'
30675 cls : 'btn-group roo-upload-cropbox-rotate-right',
30676 action : 'rotate-right',
30680 cls : 'btn btn-default',
30681 html : '<i class="fa fa-repeat"></i>'
30689 cls : 'btn-group roo-upload-cropbox-rotate-left',
30690 action : 'rotate-left',
30694 cls : 'btn btn-default',
30695 html : '<i class="fa fa-undo"></i>'
30701 cls : 'btn-group roo-upload-cropbox-rotate-right',
30702 action : 'rotate-right',
30706 cls : 'btn btn-default',
30707 html : '<i class="fa fa-repeat"></i>'
30720 * @class Roo.bootstrap.DocumentManager
30721 * @extends Roo.bootstrap.Component
30722 * Bootstrap DocumentManager class
30723 * @cfg {String} paramName default 'imageUpload'
30724 * @cfg {String} toolTipName default 'filename'
30725 * @cfg {String} method default POST
30726 * @cfg {String} url action url
30727 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30728 * @cfg {Boolean} multiple multiple upload default true
30729 * @cfg {Number} thumbSize default 300
30730 * @cfg {String} fieldLabel
30731 * @cfg {Number} labelWidth default 4
30732 * @cfg {String} labelAlign (left|top) default left
30733 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30734 * @cfg {Number} labellg set the width of label (1-12)
30735 * @cfg {Number} labelmd set the width of label (1-12)
30736 * @cfg {Number} labelsm set the width of label (1-12)
30737 * @cfg {Number} labelxs set the width of label (1-12)
30740 * Create a new DocumentManager
30741 * @param {Object} config The config object
30744 Roo.bootstrap.DocumentManager = function(config){
30745 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30748 this.delegates = [];
30753 * Fire when initial the DocumentManager
30754 * @param {Roo.bootstrap.DocumentManager} this
30759 * inspect selected file
30760 * @param {Roo.bootstrap.DocumentManager} this
30761 * @param {File} file
30766 * Fire when xhr load exception
30767 * @param {Roo.bootstrap.DocumentManager} this
30768 * @param {XMLHttpRequest} xhr
30770 "exception" : true,
30772 * @event afterupload
30773 * Fire when xhr load exception
30774 * @param {Roo.bootstrap.DocumentManager} this
30775 * @param {XMLHttpRequest} xhr
30777 "afterupload" : true,
30780 * prepare the form data
30781 * @param {Roo.bootstrap.DocumentManager} this
30782 * @param {Object} formData
30787 * Fire when remove the file
30788 * @param {Roo.bootstrap.DocumentManager} this
30789 * @param {Object} file
30794 * Fire after refresh the file
30795 * @param {Roo.bootstrap.DocumentManager} this
30800 * Fire after click the image
30801 * @param {Roo.bootstrap.DocumentManager} this
30802 * @param {Object} file
30807 * Fire when upload a image and editable set to true
30808 * @param {Roo.bootstrap.DocumentManager} this
30809 * @param {Object} file
30813 * @event beforeselectfile
30814 * Fire before select file
30815 * @param {Roo.bootstrap.DocumentManager} this
30817 "beforeselectfile" : true,
30820 * Fire before process file
30821 * @param {Roo.bootstrap.DocumentManager} this
30822 * @param {Object} file
30826 * @event previewrendered
30827 * Fire when preview rendered
30828 * @param {Roo.bootstrap.DocumentManager} this
30829 * @param {Object} file
30831 "previewrendered" : true,
30834 "previewResize" : true
30839 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30848 paramName : 'imageUpload',
30849 toolTipName : 'filename',
30852 labelAlign : 'left',
30862 getAutoCreate : function()
30864 var managerWidget = {
30866 cls : 'roo-document-manager',
30870 cls : 'roo-document-manager-selector',
30875 cls : 'roo-document-manager-uploader',
30879 cls : 'roo-document-manager-upload-btn',
30880 html : '<i class="fa fa-plus"></i>'
30891 cls : 'column col-md-12',
30896 if(this.fieldLabel.length){
30901 cls : 'column col-md-12',
30902 html : this.fieldLabel
30906 cls : 'column col-md-12',
30911 if(this.labelAlign == 'left'){
30916 html : this.fieldLabel
30925 if(this.labelWidth > 12){
30926 content[0].style = "width: " + this.labelWidth + 'px';
30929 if(this.labelWidth < 13 && this.labelmd == 0){
30930 this.labelmd = this.labelWidth;
30933 if(this.labellg > 0){
30934 content[0].cls += ' col-lg-' + this.labellg;
30935 content[1].cls += ' col-lg-' + (12 - this.labellg);
30938 if(this.labelmd > 0){
30939 content[0].cls += ' col-md-' + this.labelmd;
30940 content[1].cls += ' col-md-' + (12 - this.labelmd);
30943 if(this.labelsm > 0){
30944 content[0].cls += ' col-sm-' + this.labelsm;
30945 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30948 if(this.labelxs > 0){
30949 content[0].cls += ' col-xs-' + this.labelxs;
30950 content[1].cls += ' col-xs-' + (12 - this.labelxs);
30958 cls : 'row clearfix',
30966 initEvents : function()
30968 this.managerEl = this.el.select('.roo-document-manager', true).first();
30969 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30971 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30972 this.selectorEl.hide();
30975 this.selectorEl.attr('multiple', 'multiple');
30978 this.selectorEl.on('change', this.onFileSelected, this);
30980 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30981 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30983 this.uploader.on('click', this.onUploaderClick, this);
30985 this.renderProgressDialog();
30989 window.addEventListener("resize", function() { _this.refresh(); } );
30991 this.fireEvent('initial', this);
30994 renderProgressDialog : function()
30998 this.progressDialog = new Roo.bootstrap.Modal({
30999 cls : 'roo-document-manager-progress-dialog',
31000 allow_close : false,
31011 btnclick : function() {
31012 _this.uploadCancel();
31018 this.progressDialog.render(Roo.get(document.body));
31020 this.progress = new Roo.bootstrap.Progress({
31021 cls : 'roo-document-manager-progress',
31026 this.progress.render(this.progressDialog.getChildContainer());
31028 this.progressBar = new Roo.bootstrap.ProgressBar({
31029 cls : 'roo-document-manager-progress-bar',
31032 aria_valuemax : 12,
31036 this.progressBar.render(this.progress.getChildContainer());
31039 onUploaderClick : function(e)
31041 e.preventDefault();
31043 if(this.fireEvent('beforeselectfile', this) != false){
31044 this.selectorEl.dom.click();
31049 onFileSelected : function(e)
31051 e.preventDefault();
31053 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31057 Roo.each(this.selectorEl.dom.files, function(file){
31058 if(this.fireEvent('inspect', this, file) != false){
31059 this.files.push(file);
31069 this.selectorEl.dom.value = '';
31071 if(!this.files || !this.files.length){
31075 if(this.boxes > 0 && this.files.length > this.boxes){
31076 this.files = this.files.slice(0, this.boxes);
31079 this.uploader.show();
31081 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31082 this.uploader.hide();
31091 Roo.each(this.files, function(file){
31093 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31094 var f = this.renderPreview(file);
31099 if(file.type.indexOf('image') != -1){
31100 this.delegates.push(
31102 _this.process(file);
31103 }).createDelegate(this)
31111 _this.process(file);
31112 }).createDelegate(this)
31117 this.files = files;
31119 this.delegates = this.delegates.concat(docs);
31121 if(!this.delegates.length){
31126 this.progressBar.aria_valuemax = this.delegates.length;
31133 arrange : function()
31135 if(!this.delegates.length){
31136 this.progressDialog.hide();
31141 var delegate = this.delegates.shift();
31143 this.progressDialog.show();
31145 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31147 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31152 refresh : function()
31154 this.uploader.show();
31156 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31157 this.uploader.hide();
31160 Roo.isTouch ? this.closable(false) : this.closable(true);
31162 this.fireEvent('refresh', this);
31165 onRemove : function(e, el, o)
31167 e.preventDefault();
31169 this.fireEvent('remove', this, o);
31173 remove : function(o)
31177 Roo.each(this.files, function(file){
31178 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31187 this.files = files;
31194 Roo.each(this.files, function(file){
31199 file.target.remove();
31208 onClick : function(e, el, o)
31210 e.preventDefault();
31212 this.fireEvent('click', this, o);
31216 closable : function(closable)
31218 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31220 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31232 xhrOnLoad : function(xhr)
31234 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31238 if (xhr.readyState !== 4) {
31240 this.fireEvent('exception', this, xhr);
31244 var response = Roo.decode(xhr.responseText);
31246 if(!response.success){
31248 this.fireEvent('exception', this, xhr);
31252 var file = this.renderPreview(response.data);
31254 this.files.push(file);
31258 this.fireEvent('afterupload', this, xhr);
31262 xhrOnError : function(xhr)
31264 Roo.log('xhr on error');
31266 var response = Roo.decode(xhr.responseText);
31273 process : function(file)
31275 if(this.fireEvent('process', this, file) !== false){
31276 if(this.editable && file.type.indexOf('image') != -1){
31277 this.fireEvent('edit', this, file);
31281 this.uploadStart(file, false);
31288 uploadStart : function(file, crop)
31290 this.xhr = new XMLHttpRequest();
31292 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31297 file.xhr = this.xhr;
31299 this.managerEl.createChild({
31301 cls : 'roo-document-manager-loading',
31305 tooltip : file.name,
31306 cls : 'roo-document-manager-thumb',
31307 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31313 this.xhr.open(this.method, this.url, true);
31316 "Accept": "application/json",
31317 "Cache-Control": "no-cache",
31318 "X-Requested-With": "XMLHttpRequest"
31321 for (var headerName in headers) {
31322 var headerValue = headers[headerName];
31324 this.xhr.setRequestHeader(headerName, headerValue);
31330 this.xhr.onload = function()
31332 _this.xhrOnLoad(_this.xhr);
31335 this.xhr.onerror = function()
31337 _this.xhrOnError(_this.xhr);
31340 var formData = new FormData();
31342 formData.append('returnHTML', 'NO');
31345 formData.append('crop', crop);
31348 formData.append(this.paramName, file, file.name);
31355 if(this.fireEvent('prepare', this, formData, options) != false){
31357 if(options.manually){
31361 this.xhr.send(formData);
31365 this.uploadCancel();
31368 uploadCancel : function()
31374 this.delegates = [];
31376 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31383 renderPreview : function(file)
31385 if(typeof(file.target) != 'undefined' && file.target){
31389 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31391 var previewEl = this.managerEl.createChild({
31393 cls : 'roo-document-manager-preview',
31397 tooltip : file[this.toolTipName],
31398 cls : 'roo-document-manager-thumb',
31399 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31404 html : '<i class="fa fa-times-circle"></i>'
31409 var close = previewEl.select('button.close', true).first();
31411 close.on('click', this.onRemove, this, file);
31413 file.target = previewEl;
31415 var image = previewEl.select('img', true).first();
31419 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31421 image.on('click', this.onClick, this, file);
31423 this.fireEvent('previewrendered', this, file);
31429 onPreviewLoad : function(file, image)
31431 if(typeof(file.target) == 'undefined' || !file.target){
31435 var width = image.dom.naturalWidth || image.dom.width;
31436 var height = image.dom.naturalHeight || image.dom.height;
31438 if(!this.previewResize) {
31442 if(width > height){
31443 file.target.addClass('wide');
31447 file.target.addClass('tall');
31452 uploadFromSource : function(file, crop)
31454 this.xhr = new XMLHttpRequest();
31456 this.managerEl.createChild({
31458 cls : 'roo-document-manager-loading',
31462 tooltip : file.name,
31463 cls : 'roo-document-manager-thumb',
31464 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31470 this.xhr.open(this.method, this.url, true);
31473 "Accept": "application/json",
31474 "Cache-Control": "no-cache",
31475 "X-Requested-With": "XMLHttpRequest"
31478 for (var headerName in headers) {
31479 var headerValue = headers[headerName];
31481 this.xhr.setRequestHeader(headerName, headerValue);
31487 this.xhr.onload = function()
31489 _this.xhrOnLoad(_this.xhr);
31492 this.xhr.onerror = function()
31494 _this.xhrOnError(_this.xhr);
31497 var formData = new FormData();
31499 formData.append('returnHTML', 'NO');
31501 formData.append('crop', crop);
31503 if(typeof(file.filename) != 'undefined'){
31504 formData.append('filename', file.filename);
31507 if(typeof(file.mimetype) != 'undefined'){
31508 formData.append('mimetype', file.mimetype);
31513 if(this.fireEvent('prepare', this, formData) != false){
31514 this.xhr.send(formData);
31524 * @class Roo.bootstrap.DocumentViewer
31525 * @extends Roo.bootstrap.Component
31526 * Bootstrap DocumentViewer class
31527 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31528 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31531 * Create a new DocumentViewer
31532 * @param {Object} config The config object
31535 Roo.bootstrap.DocumentViewer = function(config){
31536 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31541 * Fire after initEvent
31542 * @param {Roo.bootstrap.DocumentViewer} this
31548 * @param {Roo.bootstrap.DocumentViewer} this
31553 * Fire after download button
31554 * @param {Roo.bootstrap.DocumentViewer} this
31559 * Fire after trash button
31560 * @param {Roo.bootstrap.DocumentViewer} this
31567 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31569 showDownload : true,
31573 getAutoCreate : function()
31577 cls : 'roo-document-viewer',
31581 cls : 'roo-document-viewer-body',
31585 cls : 'roo-document-viewer-thumb',
31589 cls : 'roo-document-viewer-image'
31597 cls : 'roo-document-viewer-footer',
31600 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31604 cls : 'btn-group roo-document-viewer-download',
31608 cls : 'btn btn-default',
31609 html : '<i class="fa fa-download"></i>'
31615 cls : 'btn-group roo-document-viewer-trash',
31619 cls : 'btn btn-default',
31620 html : '<i class="fa fa-trash"></i>'
31633 initEvents : function()
31635 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31636 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31638 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31639 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31641 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31642 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31644 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31645 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31647 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31648 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31650 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31651 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31653 this.bodyEl.on('click', this.onClick, this);
31654 this.downloadBtn.on('click', this.onDownload, this);
31655 this.trashBtn.on('click', this.onTrash, this);
31657 this.downloadBtn.hide();
31658 this.trashBtn.hide();
31660 if(this.showDownload){
31661 this.downloadBtn.show();
31664 if(this.showTrash){
31665 this.trashBtn.show();
31668 if(!this.showDownload && !this.showTrash) {
31669 this.footerEl.hide();
31674 initial : function()
31676 this.fireEvent('initial', this);
31680 onClick : function(e)
31682 e.preventDefault();
31684 this.fireEvent('click', this);
31687 onDownload : function(e)
31689 e.preventDefault();
31691 this.fireEvent('download', this);
31694 onTrash : function(e)
31696 e.preventDefault();
31698 this.fireEvent('trash', this);
31710 * @class Roo.bootstrap.NavProgressBar
31711 * @extends Roo.bootstrap.Component
31712 * Bootstrap NavProgressBar class
31715 * Create a new nav progress bar
31716 * @param {Object} config The config object
31719 Roo.bootstrap.NavProgressBar = function(config){
31720 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31722 this.bullets = this.bullets || [];
31724 // Roo.bootstrap.NavProgressBar.register(this);
31728 * Fires when the active item changes
31729 * @param {Roo.bootstrap.NavProgressBar} this
31730 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31731 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31738 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31743 getAutoCreate : function()
31745 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31749 cls : 'roo-navigation-bar-group',
31753 cls : 'roo-navigation-top-bar'
31757 cls : 'roo-navigation-bullets-bar',
31761 cls : 'roo-navigation-bar'
31768 cls : 'roo-navigation-bottom-bar'
31778 initEvents: function()
31783 onRender : function(ct, position)
31785 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31787 if(this.bullets.length){
31788 Roo.each(this.bullets, function(b){
31797 addItem : function(cfg)
31799 var item = new Roo.bootstrap.NavProgressItem(cfg);
31801 item.parentId = this.id;
31802 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31805 var top = new Roo.bootstrap.Element({
31807 cls : 'roo-navigation-bar-text'
31810 var bottom = new Roo.bootstrap.Element({
31812 cls : 'roo-navigation-bar-text'
31815 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31816 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31818 var topText = new Roo.bootstrap.Element({
31820 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31823 var bottomText = new Roo.bootstrap.Element({
31825 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31828 topText.onRender(top.el, null);
31829 bottomText.onRender(bottom.el, null);
31832 item.bottomEl = bottom;
31835 this.barItems.push(item);
31840 getActive : function()
31842 var active = false;
31844 Roo.each(this.barItems, function(v){
31846 if (!v.isActive()) {
31858 setActiveItem : function(item)
31862 Roo.each(this.barItems, function(v){
31863 if (v.rid == item.rid) {
31867 if (v.isActive()) {
31868 v.setActive(false);
31873 item.setActive(true);
31875 this.fireEvent('changed', this, item, prev);
31878 getBarItem: function(rid)
31882 Roo.each(this.barItems, function(e) {
31883 if (e.rid != rid) {
31894 indexOfItem : function(item)
31898 Roo.each(this.barItems, function(v, i){
31900 if (v.rid != item.rid) {
31911 setActiveNext : function()
31913 var i = this.indexOfItem(this.getActive());
31915 if (i > this.barItems.length) {
31919 this.setActiveItem(this.barItems[i+1]);
31922 setActivePrev : function()
31924 var i = this.indexOfItem(this.getActive());
31930 this.setActiveItem(this.barItems[i-1]);
31933 format : function()
31935 if(!this.barItems.length){
31939 var width = 100 / this.barItems.length;
31941 Roo.each(this.barItems, function(i){
31942 i.el.setStyle('width', width + '%');
31943 i.topEl.el.setStyle('width', width + '%');
31944 i.bottomEl.el.setStyle('width', width + '%');
31953 * Nav Progress Item
31958 * @class Roo.bootstrap.NavProgressItem
31959 * @extends Roo.bootstrap.Component
31960 * Bootstrap NavProgressItem class
31961 * @cfg {String} rid the reference id
31962 * @cfg {Boolean} active (true|false) Is item active default false
31963 * @cfg {Boolean} disabled (true|false) Is item active default false
31964 * @cfg {String} html
31965 * @cfg {String} position (top|bottom) text position default bottom
31966 * @cfg {String} icon show icon instead of number
31969 * Create a new NavProgressItem
31970 * @param {Object} config The config object
31972 Roo.bootstrap.NavProgressItem = function(config){
31973 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31978 * The raw click event for the entire grid.
31979 * @param {Roo.bootstrap.NavProgressItem} this
31980 * @param {Roo.EventObject} e
31987 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
31993 position : 'bottom',
31996 getAutoCreate : function()
31998 var iconCls = 'roo-navigation-bar-item-icon';
32000 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32004 cls: 'roo-navigation-bar-item',
32014 cfg.cls += ' active';
32017 cfg.cls += ' disabled';
32023 disable : function()
32025 this.setDisabled(true);
32028 enable : function()
32030 this.setDisabled(false);
32033 initEvents: function()
32035 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32037 this.iconEl.on('click', this.onClick, this);
32040 onClick : function(e)
32042 e.preventDefault();
32048 if(this.fireEvent('click', this, e) === false){
32052 this.parent().setActiveItem(this);
32055 isActive: function ()
32057 return this.active;
32060 setActive : function(state)
32062 if(this.active == state){
32066 this.active = state;
32069 this.el.addClass('active');
32073 this.el.removeClass('active');
32078 setDisabled : function(state)
32080 if(this.disabled == state){
32084 this.disabled = state;
32087 this.el.addClass('disabled');
32091 this.el.removeClass('disabled');
32094 tooltipEl : function()
32096 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32109 * @class Roo.bootstrap.FieldLabel
32110 * @extends Roo.bootstrap.Component
32111 * Bootstrap FieldLabel class
32112 * @cfg {String} html contents of the element
32113 * @cfg {String} tag tag of the element default label
32114 * @cfg {String} cls class of the element
32115 * @cfg {String} target label target
32116 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32117 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32118 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32119 * @cfg {String} iconTooltip default "This field is required"
32120 * @cfg {String} indicatorpos (left|right) default left
32123 * Create a new FieldLabel
32124 * @param {Object} config The config object
32127 Roo.bootstrap.FieldLabel = function(config){
32128 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32133 * Fires after the field has been marked as invalid.
32134 * @param {Roo.form.FieldLabel} this
32135 * @param {String} msg The validation message
32140 * Fires after the field has been validated with no errors.
32141 * @param {Roo.form.FieldLabel} this
32147 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32154 invalidClass : 'has-warning',
32155 validClass : 'has-success',
32156 iconTooltip : 'This field is required',
32157 indicatorpos : 'left',
32159 getAutoCreate : function(){
32162 if (!this.allowBlank) {
32168 cls : 'roo-bootstrap-field-label ' + this.cls,
32173 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32174 tooltip : this.iconTooltip
32183 if(this.indicatorpos == 'right'){
32186 cls : 'roo-bootstrap-field-label ' + this.cls,
32195 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32196 tooltip : this.iconTooltip
32205 initEvents: function()
32207 Roo.bootstrap.Element.superclass.initEvents.call(this);
32209 this.indicator = this.indicatorEl();
32211 if(this.indicator){
32212 this.indicator.removeClass('visible');
32213 this.indicator.addClass('invisible');
32216 Roo.bootstrap.FieldLabel.register(this);
32219 indicatorEl : function()
32221 var indicator = this.el.select('i.roo-required-indicator',true).first();
32232 * Mark this field as valid
32234 markValid : function()
32236 if(this.indicator){
32237 this.indicator.removeClass('visible');
32238 this.indicator.addClass('invisible');
32240 if (Roo.bootstrap.version == 3) {
32241 this.el.removeClass(this.invalidClass);
32242 this.el.addClass(this.validClass);
32244 this.el.removeClass('is-invalid');
32245 this.el.addClass('is-valid');
32249 this.fireEvent('valid', this);
32253 * Mark this field as invalid
32254 * @param {String} msg The validation message
32256 markInvalid : function(msg)
32258 if(this.indicator){
32259 this.indicator.removeClass('invisible');
32260 this.indicator.addClass('visible');
32262 if (Roo.bootstrap.version == 3) {
32263 this.el.removeClass(this.validClass);
32264 this.el.addClass(this.invalidClass);
32266 this.el.removeClass('is-valid');
32267 this.el.addClass('is-invalid');
32271 this.fireEvent('invalid', this, msg);
32277 Roo.apply(Roo.bootstrap.FieldLabel, {
32282 * register a FieldLabel Group
32283 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32285 register : function(label)
32287 if(this.groups.hasOwnProperty(label.target)){
32291 this.groups[label.target] = label;
32295 * fetch a FieldLabel Group based on the target
32296 * @param {string} target
32297 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32299 get: function(target) {
32300 if (typeof(this.groups[target]) == 'undefined') {
32304 return this.groups[target] ;
32313 * page DateSplitField.
32319 * @class Roo.bootstrap.DateSplitField
32320 * @extends Roo.bootstrap.Component
32321 * Bootstrap DateSplitField class
32322 * @cfg {string} fieldLabel - the label associated
32323 * @cfg {Number} labelWidth set the width of label (0-12)
32324 * @cfg {String} labelAlign (top|left)
32325 * @cfg {Boolean} dayAllowBlank (true|false) default false
32326 * @cfg {Boolean} monthAllowBlank (true|false) default false
32327 * @cfg {Boolean} yearAllowBlank (true|false) default false
32328 * @cfg {string} dayPlaceholder
32329 * @cfg {string} monthPlaceholder
32330 * @cfg {string} yearPlaceholder
32331 * @cfg {string} dayFormat default 'd'
32332 * @cfg {string} monthFormat default 'm'
32333 * @cfg {string} yearFormat default 'Y'
32334 * @cfg {Number} labellg set the width of label (1-12)
32335 * @cfg {Number} labelmd set the width of label (1-12)
32336 * @cfg {Number} labelsm set the width of label (1-12)
32337 * @cfg {Number} labelxs set the width of label (1-12)
32341 * Create a new DateSplitField
32342 * @param {Object} config The config object
32345 Roo.bootstrap.DateSplitField = function(config){
32346 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32352 * getting the data of years
32353 * @param {Roo.bootstrap.DateSplitField} this
32354 * @param {Object} years
32359 * getting the data of days
32360 * @param {Roo.bootstrap.DateSplitField} this
32361 * @param {Object} days
32366 * Fires after the field has been marked as invalid.
32367 * @param {Roo.form.Field} this
32368 * @param {String} msg The validation message
32373 * Fires after the field has been validated with no errors.
32374 * @param {Roo.form.Field} this
32380 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32383 labelAlign : 'top',
32385 dayAllowBlank : false,
32386 monthAllowBlank : false,
32387 yearAllowBlank : false,
32388 dayPlaceholder : '',
32389 monthPlaceholder : '',
32390 yearPlaceholder : '',
32394 isFormField : true,
32400 getAutoCreate : function()
32404 cls : 'row roo-date-split-field-group',
32409 cls : 'form-hidden-field roo-date-split-field-group-value',
32415 var labelCls = 'col-md-12';
32416 var contentCls = 'col-md-4';
32418 if(this.fieldLabel){
32422 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32426 html : this.fieldLabel
32431 if(this.labelAlign == 'left'){
32433 if(this.labelWidth > 12){
32434 label.style = "width: " + this.labelWidth + 'px';
32437 if(this.labelWidth < 13 && this.labelmd == 0){
32438 this.labelmd = this.labelWidth;
32441 if(this.labellg > 0){
32442 labelCls = ' col-lg-' + this.labellg;
32443 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32446 if(this.labelmd > 0){
32447 labelCls = ' col-md-' + this.labelmd;
32448 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32451 if(this.labelsm > 0){
32452 labelCls = ' col-sm-' + this.labelsm;
32453 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32456 if(this.labelxs > 0){
32457 labelCls = ' col-xs-' + this.labelxs;
32458 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32462 label.cls += ' ' + labelCls;
32464 cfg.cn.push(label);
32467 Roo.each(['day', 'month', 'year'], function(t){
32470 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32477 inputEl: function ()
32479 return this.el.select('.roo-date-split-field-group-value', true).first();
32482 onRender : function(ct, position)
32486 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32488 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32490 this.dayField = new Roo.bootstrap.ComboBox({
32491 allowBlank : this.dayAllowBlank,
32492 alwaysQuery : true,
32493 displayField : 'value',
32496 forceSelection : true,
32498 placeholder : this.dayPlaceholder,
32499 selectOnFocus : true,
32500 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32501 triggerAction : 'all',
32503 valueField : 'value',
32504 store : new Roo.data.SimpleStore({
32505 data : (function() {
32507 _this.fireEvent('days', _this, days);
32510 fields : [ 'value' ]
32513 select : function (_self, record, index)
32515 _this.setValue(_this.getValue());
32520 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32522 this.monthField = new Roo.bootstrap.MonthField({
32523 after : '<i class=\"fa fa-calendar\"></i>',
32524 allowBlank : this.monthAllowBlank,
32525 placeholder : this.monthPlaceholder,
32528 render : function (_self)
32530 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32531 e.preventDefault();
32535 select : function (_self, oldvalue, newvalue)
32537 _this.setValue(_this.getValue());
32542 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32544 this.yearField = new Roo.bootstrap.ComboBox({
32545 allowBlank : this.yearAllowBlank,
32546 alwaysQuery : true,
32547 displayField : 'value',
32550 forceSelection : true,
32552 placeholder : this.yearPlaceholder,
32553 selectOnFocus : true,
32554 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32555 triggerAction : 'all',
32557 valueField : 'value',
32558 store : new Roo.data.SimpleStore({
32559 data : (function() {
32561 _this.fireEvent('years', _this, years);
32564 fields : [ 'value' ]
32567 select : function (_self, record, index)
32569 _this.setValue(_this.getValue());
32574 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32577 setValue : function(v, format)
32579 this.inputEl.dom.value = v;
32581 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32583 var d = Date.parseDate(v, f);
32590 this.setDay(d.format(this.dayFormat));
32591 this.setMonth(d.format(this.monthFormat));
32592 this.setYear(d.format(this.yearFormat));
32599 setDay : function(v)
32601 this.dayField.setValue(v);
32602 this.inputEl.dom.value = this.getValue();
32607 setMonth : function(v)
32609 this.monthField.setValue(v, true);
32610 this.inputEl.dom.value = this.getValue();
32615 setYear : function(v)
32617 this.yearField.setValue(v);
32618 this.inputEl.dom.value = this.getValue();
32623 getDay : function()
32625 return this.dayField.getValue();
32628 getMonth : function()
32630 return this.monthField.getValue();
32633 getYear : function()
32635 return this.yearField.getValue();
32638 getValue : function()
32640 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32642 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32652 this.inputEl.dom.value = '';
32657 validate : function()
32659 var d = this.dayField.validate();
32660 var m = this.monthField.validate();
32661 var y = this.yearField.validate();
32666 (!this.dayAllowBlank && !d) ||
32667 (!this.monthAllowBlank && !m) ||
32668 (!this.yearAllowBlank && !y)
32673 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32682 this.markInvalid();
32687 markValid : function()
32690 var label = this.el.select('label', true).first();
32691 var icon = this.el.select('i.fa-star', true).first();
32697 this.fireEvent('valid', this);
32701 * Mark this field as invalid
32702 * @param {String} msg The validation message
32704 markInvalid : function(msg)
32707 var label = this.el.select('label', true).first();
32708 var icon = this.el.select('i.fa-star', true).first();
32710 if(label && !icon){
32711 this.el.select('.roo-date-split-field-label', true).createChild({
32713 cls : 'text-danger fa fa-lg fa-star',
32714 tooltip : 'This field is required',
32715 style : 'margin-right:5px;'
32719 this.fireEvent('invalid', this, msg);
32722 clearInvalid : function()
32724 var label = this.el.select('label', true).first();
32725 var icon = this.el.select('i.fa-star', true).first();
32731 this.fireEvent('valid', this);
32734 getName: function()
32744 * http://masonry.desandro.com
32746 * The idea is to render all the bricks based on vertical width...
32748 * The original code extends 'outlayer' - we might need to use that....
32754 * @class Roo.bootstrap.LayoutMasonry
32755 * @extends Roo.bootstrap.Component
32756 * Bootstrap Layout Masonry class
32759 * Create a new Element
32760 * @param {Object} config The config object
32763 Roo.bootstrap.LayoutMasonry = function(config){
32765 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32769 Roo.bootstrap.LayoutMasonry.register(this);
32775 * Fire after layout the items
32776 * @param {Roo.bootstrap.LayoutMasonry} this
32777 * @param {Roo.EventObject} e
32784 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32787 * @cfg {Boolean} isLayoutInstant = no animation?
32789 isLayoutInstant : false, // needed?
32792 * @cfg {Number} boxWidth width of the columns
32797 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32802 * @cfg {Number} padWidth padding below box..
32807 * @cfg {Number} gutter gutter width..
32812 * @cfg {Number} maxCols maximum number of columns
32818 * @cfg {Boolean} isAutoInitial defalut true
32820 isAutoInitial : true,
32825 * @cfg {Boolean} isHorizontal defalut false
32827 isHorizontal : false,
32829 currentSize : null,
32835 bricks: null, //CompositeElement
32839 _isLayoutInited : false,
32841 // isAlternative : false, // only use for vertical layout...
32844 * @cfg {Number} alternativePadWidth padding below box..
32846 alternativePadWidth : 50,
32848 selectedBrick : [],
32850 getAutoCreate : function(){
32852 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32856 cls: 'blog-masonary-wrapper ' + this.cls,
32858 cls : 'mas-boxes masonary'
32865 getChildContainer: function( )
32867 if (this.boxesEl) {
32868 return this.boxesEl;
32871 this.boxesEl = this.el.select('.mas-boxes').first();
32873 return this.boxesEl;
32877 initEvents : function()
32881 if(this.isAutoInitial){
32882 Roo.log('hook children rendered');
32883 this.on('childrenrendered', function() {
32884 Roo.log('children rendered');
32890 initial : function()
32892 this.selectedBrick = [];
32894 this.currentSize = this.el.getBox(true);
32896 Roo.EventManager.onWindowResize(this.resize, this);
32898 if(!this.isAutoInitial){
32906 //this.layout.defer(500,this);
32910 resize : function()
32912 var cs = this.el.getBox(true);
32915 this.currentSize.width == cs.width &&
32916 this.currentSize.x == cs.x &&
32917 this.currentSize.height == cs.height &&
32918 this.currentSize.y == cs.y
32920 Roo.log("no change in with or X or Y");
32924 this.currentSize = cs;
32930 layout : function()
32932 this._resetLayout();
32934 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32936 this.layoutItems( isInstant );
32938 this._isLayoutInited = true;
32940 this.fireEvent('layout', this);
32944 _resetLayout : function()
32946 if(this.isHorizontal){
32947 this.horizontalMeasureColumns();
32951 this.verticalMeasureColumns();
32955 verticalMeasureColumns : function()
32957 this.getContainerWidth();
32959 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32960 // this.colWidth = Math.floor(this.containerWidth * 0.8);
32964 var boxWidth = this.boxWidth + this.padWidth;
32966 if(this.containerWidth < this.boxWidth){
32967 boxWidth = this.containerWidth
32970 var containerWidth = this.containerWidth;
32972 var cols = Math.floor(containerWidth / boxWidth);
32974 this.cols = Math.max( cols, 1 );
32976 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32978 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32980 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32982 this.colWidth = boxWidth + avail - this.padWidth;
32984 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32985 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
32988 horizontalMeasureColumns : function()
32990 this.getContainerWidth();
32992 var boxWidth = this.boxWidth;
32994 if(this.containerWidth < boxWidth){
32995 boxWidth = this.containerWidth;
32998 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33000 this.el.setHeight(boxWidth);
33004 getContainerWidth : function()
33006 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33009 layoutItems : function( isInstant )
33011 Roo.log(this.bricks);
33013 var items = Roo.apply([], this.bricks);
33015 if(this.isHorizontal){
33016 this._horizontalLayoutItems( items , isInstant );
33020 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33021 // this._verticalAlternativeLayoutItems( items , isInstant );
33025 this._verticalLayoutItems( items , isInstant );
33029 _verticalLayoutItems : function ( items , isInstant)
33031 if ( !items || !items.length ) {
33036 ['xs', 'xs', 'xs', 'tall'],
33037 ['xs', 'xs', 'tall'],
33038 ['xs', 'xs', 'sm'],
33039 ['xs', 'xs', 'xs'],
33045 ['sm', 'xs', 'xs'],
33049 ['tall', 'xs', 'xs', 'xs'],
33050 ['tall', 'xs', 'xs'],
33062 Roo.each(items, function(item, k){
33064 switch (item.size) {
33065 // these layouts take up a full box,
33076 boxes.push([item]);
33099 var filterPattern = function(box, length)
33107 var pattern = box.slice(0, length);
33111 Roo.each(pattern, function(i){
33112 format.push(i.size);
33115 Roo.each(standard, function(s){
33117 if(String(s) != String(format)){
33126 if(!match && length == 1){
33131 filterPattern(box, length - 1);
33135 queue.push(pattern);
33137 box = box.slice(length, box.length);
33139 filterPattern(box, 4);
33145 Roo.each(boxes, function(box, k){
33151 if(box.length == 1){
33156 filterPattern(box, 4);
33160 this._processVerticalLayoutQueue( queue, isInstant );
33164 // _verticalAlternativeLayoutItems : function( items , isInstant )
33166 // if ( !items || !items.length ) {
33170 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33174 _horizontalLayoutItems : function ( items , isInstant)
33176 if ( !items || !items.length || items.length < 3) {
33182 var eItems = items.slice(0, 3);
33184 items = items.slice(3, items.length);
33187 ['xs', 'xs', 'xs', 'wide'],
33188 ['xs', 'xs', 'wide'],
33189 ['xs', 'xs', 'sm'],
33190 ['xs', 'xs', 'xs'],
33196 ['sm', 'xs', 'xs'],
33200 ['wide', 'xs', 'xs', 'xs'],
33201 ['wide', 'xs', 'xs'],
33214 Roo.each(items, function(item, k){
33216 switch (item.size) {
33227 boxes.push([item]);
33251 var filterPattern = function(box, length)
33259 var pattern = box.slice(0, length);
33263 Roo.each(pattern, function(i){
33264 format.push(i.size);
33267 Roo.each(standard, function(s){
33269 if(String(s) != String(format)){
33278 if(!match && length == 1){
33283 filterPattern(box, length - 1);
33287 queue.push(pattern);
33289 box = box.slice(length, box.length);
33291 filterPattern(box, 4);
33297 Roo.each(boxes, function(box, k){
33303 if(box.length == 1){
33308 filterPattern(box, 4);
33315 var pos = this.el.getBox(true);
33319 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33321 var hit_end = false;
33323 Roo.each(queue, function(box){
33327 Roo.each(box, function(b){
33329 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33339 Roo.each(box, function(b){
33341 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33344 mx = Math.max(mx, b.x);
33348 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33352 Roo.each(box, function(b){
33354 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33368 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33371 /** Sets position of item in DOM
33372 * @param {Element} item
33373 * @param {Number} x - horizontal position
33374 * @param {Number} y - vertical position
33375 * @param {Boolean} isInstant - disables transitions
33377 _processVerticalLayoutQueue : function( queue, isInstant )
33379 var pos = this.el.getBox(true);
33384 for (var i = 0; i < this.cols; i++){
33388 Roo.each(queue, function(box, k){
33390 var col = k % this.cols;
33392 Roo.each(box, function(b,kk){
33394 b.el.position('absolute');
33396 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33397 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33399 if(b.size == 'md-left' || b.size == 'md-right'){
33400 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33401 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33404 b.el.setWidth(width);
33405 b.el.setHeight(height);
33407 b.el.select('iframe',true).setSize(width,height);
33411 for (var i = 0; i < this.cols; i++){
33413 if(maxY[i] < maxY[col]){
33418 col = Math.min(col, i);
33422 x = pos.x + col * (this.colWidth + this.padWidth);
33426 var positions = [];
33428 switch (box.length){
33430 positions = this.getVerticalOneBoxColPositions(x, y, box);
33433 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33436 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33439 positions = this.getVerticalFourBoxColPositions(x, y, box);
33445 Roo.each(box, function(b,kk){
33447 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33449 var sz = b.el.getSize();
33451 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33459 for (var i = 0; i < this.cols; i++){
33460 mY = Math.max(mY, maxY[i]);
33463 this.el.setHeight(mY - pos.y);
33467 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33469 // var pos = this.el.getBox(true);
33472 // var maxX = pos.right;
33474 // var maxHeight = 0;
33476 // Roo.each(items, function(item, k){
33480 // item.el.position('absolute');
33482 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33484 // item.el.setWidth(width);
33486 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33488 // item.el.setHeight(height);
33491 // item.el.setXY([x, y], isInstant ? false : true);
33493 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33496 // y = y + height + this.alternativePadWidth;
33498 // maxHeight = maxHeight + height + this.alternativePadWidth;
33502 // this.el.setHeight(maxHeight);
33506 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33508 var pos = this.el.getBox(true);
33513 var maxX = pos.right;
33515 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33517 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33519 Roo.each(queue, function(box, k){
33521 Roo.each(box, function(b, kk){
33523 b.el.position('absolute');
33525 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33526 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33528 if(b.size == 'md-left' || b.size == 'md-right'){
33529 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33530 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33533 b.el.setWidth(width);
33534 b.el.setHeight(height);
33542 var positions = [];
33544 switch (box.length){
33546 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33549 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33552 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33555 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33561 Roo.each(box, function(b,kk){
33563 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33565 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33573 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33575 Roo.each(eItems, function(b,k){
33577 b.size = (k == 0) ? 'sm' : 'xs';
33578 b.x = (k == 0) ? 2 : 1;
33579 b.y = (k == 0) ? 2 : 1;
33581 b.el.position('absolute');
33583 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33585 b.el.setWidth(width);
33587 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33589 b.el.setHeight(height);
33593 var positions = [];
33596 x : maxX - this.unitWidth * 2 - this.gutter,
33601 x : maxX - this.unitWidth,
33602 y : minY + (this.unitWidth + this.gutter) * 2
33606 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33610 Roo.each(eItems, function(b,k){
33612 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33618 getVerticalOneBoxColPositions : function(x, y, box)
33622 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33624 if(box[0].size == 'md-left'){
33628 if(box[0].size == 'md-right'){
33633 x : x + (this.unitWidth + this.gutter) * rand,
33640 getVerticalTwoBoxColPositions : function(x, y, box)
33644 if(box[0].size == 'xs'){
33648 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33652 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33666 x : x + (this.unitWidth + this.gutter) * 2,
33667 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33674 getVerticalThreeBoxColPositions : function(x, y, box)
33678 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33686 x : x + (this.unitWidth + this.gutter) * 1,
33691 x : x + (this.unitWidth + this.gutter) * 2,
33699 if(box[0].size == 'xs' && box[1].size == 'xs'){
33708 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33712 x : x + (this.unitWidth + this.gutter) * 1,
33726 x : x + (this.unitWidth + this.gutter) * 2,
33731 x : x + (this.unitWidth + this.gutter) * 2,
33732 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33739 getVerticalFourBoxColPositions : function(x, y, box)
33743 if(box[0].size == 'xs'){
33752 y : y + (this.unitHeight + this.gutter) * 1
33757 y : y + (this.unitHeight + this.gutter) * 2
33761 x : x + (this.unitWidth + this.gutter) * 1,
33775 x : x + (this.unitWidth + this.gutter) * 2,
33780 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33781 y : y + (this.unitHeight + this.gutter) * 1
33785 x : x + (this.unitWidth + this.gutter) * 2,
33786 y : y + (this.unitWidth + this.gutter) * 2
33793 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33797 if(box[0].size == 'md-left'){
33799 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33806 if(box[0].size == 'md-right'){
33808 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33809 y : minY + (this.unitWidth + this.gutter) * 1
33815 var rand = Math.floor(Math.random() * (4 - box[0].y));
33818 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33819 y : minY + (this.unitWidth + this.gutter) * rand
33826 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33830 if(box[0].size == 'xs'){
33833 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33838 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33839 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33847 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33852 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33853 y : minY + (this.unitWidth + this.gutter) * 2
33860 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33864 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33867 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33872 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33873 y : minY + (this.unitWidth + this.gutter) * 1
33877 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33878 y : minY + (this.unitWidth + this.gutter) * 2
33885 if(box[0].size == 'xs' && box[1].size == 'xs'){
33888 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33893 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33898 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33899 y : minY + (this.unitWidth + this.gutter) * 1
33907 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33912 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33913 y : minY + (this.unitWidth + this.gutter) * 2
33917 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33918 y : minY + (this.unitWidth + this.gutter) * 2
33925 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33929 if(box[0].size == 'xs'){
33932 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33937 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33942 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),
33947 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33948 y : minY + (this.unitWidth + this.gutter) * 1
33956 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33961 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33962 y : minY + (this.unitWidth + this.gutter) * 2
33966 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33967 y : minY + (this.unitWidth + this.gutter) * 2
33971 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),
33972 y : minY + (this.unitWidth + this.gutter) * 2
33980 * remove a Masonry Brick
33981 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33983 removeBrick : function(brick_id)
33989 for (var i = 0; i<this.bricks.length; i++) {
33990 if (this.bricks[i].id == brick_id) {
33991 this.bricks.splice(i,1);
33992 this.el.dom.removeChild(Roo.get(brick_id).dom);
33999 * adds a Masonry Brick
34000 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34002 addBrick : function(cfg)
34004 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34005 //this.register(cn);
34006 cn.parentId = this.id;
34007 cn.render(this.el);
34012 * register a Masonry Brick
34013 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34016 register : function(brick)
34018 this.bricks.push(brick);
34019 brick.masonryId = this.id;
34023 * clear all the Masonry Brick
34025 clearAll : function()
34028 //this.getChildContainer().dom.innerHTML = "";
34029 this.el.dom.innerHTML = '';
34032 getSelected : function()
34034 if (!this.selectedBrick) {
34038 return this.selectedBrick;
34042 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34046 * register a Masonry Layout
34047 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34050 register : function(layout)
34052 this.groups[layout.id] = layout;
34055 * fetch a Masonry Layout based on the masonry layout ID
34056 * @param {string} the masonry layout to add
34057 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34060 get: function(layout_id) {
34061 if (typeof(this.groups[layout_id]) == 'undefined') {
34064 return this.groups[layout_id] ;
34076 * http://masonry.desandro.com
34078 * The idea is to render all the bricks based on vertical width...
34080 * The original code extends 'outlayer' - we might need to use that....
34086 * @class Roo.bootstrap.LayoutMasonryAuto
34087 * @extends Roo.bootstrap.Component
34088 * Bootstrap Layout Masonry class
34091 * Create a new Element
34092 * @param {Object} config The config object
34095 Roo.bootstrap.LayoutMasonryAuto = function(config){
34096 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34099 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34102 * @cfg {Boolean} isFitWidth - resize the width..
34104 isFitWidth : false, // options..
34106 * @cfg {Boolean} isOriginLeft = left align?
34108 isOriginLeft : true,
34110 * @cfg {Boolean} isOriginTop = top align?
34112 isOriginTop : false,
34114 * @cfg {Boolean} isLayoutInstant = no animation?
34116 isLayoutInstant : false, // needed?
34118 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34120 isResizingContainer : true,
34122 * @cfg {Number} columnWidth width of the columns
34128 * @cfg {Number} maxCols maximum number of columns
34133 * @cfg {Number} padHeight padding below box..
34139 * @cfg {Boolean} isAutoInitial defalut true
34142 isAutoInitial : true,
34148 initialColumnWidth : 0,
34149 currentSize : null,
34151 colYs : null, // array.
34158 bricks: null, //CompositeElement
34159 cols : 0, // array?
34160 // element : null, // wrapped now this.el
34161 _isLayoutInited : null,
34164 getAutoCreate : function(){
34168 cls: 'blog-masonary-wrapper ' + this.cls,
34170 cls : 'mas-boxes masonary'
34177 getChildContainer: function( )
34179 if (this.boxesEl) {
34180 return this.boxesEl;
34183 this.boxesEl = this.el.select('.mas-boxes').first();
34185 return this.boxesEl;
34189 initEvents : function()
34193 if(this.isAutoInitial){
34194 Roo.log('hook children rendered');
34195 this.on('childrenrendered', function() {
34196 Roo.log('children rendered');
34203 initial : function()
34205 this.reloadItems();
34207 this.currentSize = this.el.getBox(true);
34209 /// was window resize... - let's see if this works..
34210 Roo.EventManager.onWindowResize(this.resize, this);
34212 if(!this.isAutoInitial){
34217 this.layout.defer(500,this);
34220 reloadItems: function()
34222 this.bricks = this.el.select('.masonry-brick', true);
34224 this.bricks.each(function(b) {
34225 //Roo.log(b.getSize());
34226 if (!b.attr('originalwidth')) {
34227 b.attr('originalwidth', b.getSize().width);
34232 Roo.log(this.bricks.elements.length);
34235 resize : function()
34238 var cs = this.el.getBox(true);
34240 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34241 Roo.log("no change in with or X");
34244 this.currentSize = cs;
34248 layout : function()
34251 this._resetLayout();
34252 //this._manageStamps();
34254 // don't animate first layout
34255 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34256 this.layoutItems( isInstant );
34258 // flag for initalized
34259 this._isLayoutInited = true;
34262 layoutItems : function( isInstant )
34264 //var items = this._getItemsForLayout( this.items );
34265 // original code supports filtering layout items.. we just ignore it..
34267 this._layoutItems( this.bricks , isInstant );
34269 this._postLayout();
34271 _layoutItems : function ( items , isInstant)
34273 //this.fireEvent( 'layout', this, items );
34276 if ( !items || !items.elements.length ) {
34277 // no items, emit event with empty array
34282 items.each(function(item) {
34283 Roo.log("layout item");
34285 // get x/y object from method
34286 var position = this._getItemLayoutPosition( item );
34288 position.item = item;
34289 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34290 queue.push( position );
34293 this._processLayoutQueue( queue );
34295 /** Sets position of item in DOM
34296 * @param {Element} item
34297 * @param {Number} x - horizontal position
34298 * @param {Number} y - vertical position
34299 * @param {Boolean} isInstant - disables transitions
34301 _processLayoutQueue : function( queue )
34303 for ( var i=0, len = queue.length; i < len; i++ ) {
34304 var obj = queue[i];
34305 obj.item.position('absolute');
34306 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34312 * Any logic you want to do after each layout,
34313 * i.e. size the container
34315 _postLayout : function()
34317 this.resizeContainer();
34320 resizeContainer : function()
34322 if ( !this.isResizingContainer ) {
34325 var size = this._getContainerSize();
34327 this.el.setSize(size.width,size.height);
34328 this.boxesEl.setSize(size.width,size.height);
34334 _resetLayout : function()
34336 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34337 this.colWidth = this.el.getWidth();
34338 //this.gutter = this.el.getWidth();
34340 this.measureColumns();
34346 this.colYs.push( 0 );
34352 measureColumns : function()
34354 this.getContainerWidth();
34355 // if columnWidth is 0, default to outerWidth of first item
34356 if ( !this.columnWidth ) {
34357 var firstItem = this.bricks.first();
34358 Roo.log(firstItem);
34359 this.columnWidth = this.containerWidth;
34360 if (firstItem && firstItem.attr('originalwidth') ) {
34361 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34363 // columnWidth fall back to item of first element
34364 Roo.log("set column width?");
34365 this.initialColumnWidth = this.columnWidth ;
34367 // if first elem has no width, default to size of container
34372 if (this.initialColumnWidth) {
34373 this.columnWidth = this.initialColumnWidth;
34378 // column width is fixed at the top - however if container width get's smaller we should
34381 // this bit calcs how man columns..
34383 var columnWidth = this.columnWidth += this.gutter;
34385 // calculate columns
34386 var containerWidth = this.containerWidth + this.gutter;
34388 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34389 // fix rounding errors, typically with gutters
34390 var excess = columnWidth - containerWidth % columnWidth;
34393 // if overshoot is less than a pixel, round up, otherwise floor it
34394 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34395 cols = Math[ mathMethod ]( cols );
34396 this.cols = Math.max( cols, 1 );
34397 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34399 // padding positioning..
34400 var totalColWidth = this.cols * this.columnWidth;
34401 var padavail = this.containerWidth - totalColWidth;
34402 // so for 2 columns - we need 3 'pads'
34404 var padNeeded = (1+this.cols) * this.padWidth;
34406 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34408 this.columnWidth += padExtra
34409 //this.padWidth = Math.floor(padavail / ( this.cols));
34411 // adjust colum width so that padding is fixed??
34413 // we have 3 columns ... total = width * 3
34414 // we have X left over... that should be used by
34416 //if (this.expandC) {
34424 getContainerWidth : function()
34426 /* // container is parent if fit width
34427 var container = this.isFitWidth ? this.element.parentNode : this.element;
34428 // check that this.size and size are there
34429 // IE8 triggers resize on body size change, so they might not be
34431 var size = getSize( container ); //FIXME
34432 this.containerWidth = size && size.innerWidth; //FIXME
34435 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34439 _getItemLayoutPosition : function( item ) // what is item?
34441 // we resize the item to our columnWidth..
34443 item.setWidth(this.columnWidth);
34444 item.autoBoxAdjust = false;
34446 var sz = item.getSize();
34448 // how many columns does this brick span
34449 var remainder = this.containerWidth % this.columnWidth;
34451 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34452 // round if off by 1 pixel, otherwise use ceil
34453 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34454 colSpan = Math.min( colSpan, this.cols );
34456 // normally this should be '1' as we dont' currently allow multi width columns..
34458 var colGroup = this._getColGroup( colSpan );
34459 // get the minimum Y value from the columns
34460 var minimumY = Math.min.apply( Math, colGroup );
34461 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34463 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34465 // position the brick
34467 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34468 y: this.currentSize.y + minimumY + this.padHeight
34472 // apply setHeight to necessary columns
34473 var setHeight = minimumY + sz.height + this.padHeight;
34474 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34476 var setSpan = this.cols + 1 - colGroup.length;
34477 for ( var i = 0; i < setSpan; i++ ) {
34478 this.colYs[ shortColIndex + i ] = setHeight ;
34485 * @param {Number} colSpan - number of columns the element spans
34486 * @returns {Array} colGroup
34488 _getColGroup : function( colSpan )
34490 if ( colSpan < 2 ) {
34491 // if brick spans only one column, use all the column Ys
34496 // how many different places could this brick fit horizontally
34497 var groupCount = this.cols + 1 - colSpan;
34498 // for each group potential horizontal position
34499 for ( var i = 0; i < groupCount; i++ ) {
34500 // make an array of colY values for that one group
34501 var groupColYs = this.colYs.slice( i, i + colSpan );
34502 // and get the max value of the array
34503 colGroup[i] = Math.max.apply( Math, groupColYs );
34508 _manageStamp : function( stamp )
34510 var stampSize = stamp.getSize();
34511 var offset = stamp.getBox();
34512 // get the columns that this stamp affects
34513 var firstX = this.isOriginLeft ? offset.x : offset.right;
34514 var lastX = firstX + stampSize.width;
34515 var firstCol = Math.floor( firstX / this.columnWidth );
34516 firstCol = Math.max( 0, firstCol );
34518 var lastCol = Math.floor( lastX / this.columnWidth );
34519 // lastCol should not go over if multiple of columnWidth #425
34520 lastCol -= lastX % this.columnWidth ? 0 : 1;
34521 lastCol = Math.min( this.cols - 1, lastCol );
34523 // set colYs to bottom of the stamp
34524 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34527 for ( var i = firstCol; i <= lastCol; i++ ) {
34528 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34533 _getContainerSize : function()
34535 this.maxY = Math.max.apply( Math, this.colYs );
34540 if ( this.isFitWidth ) {
34541 size.width = this._getContainerFitWidth();
34547 _getContainerFitWidth : function()
34549 var unusedCols = 0;
34550 // count unused columns
34553 if ( this.colYs[i] !== 0 ) {
34558 // fit container to columns that have been used
34559 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34562 needsResizeLayout : function()
34564 var previousWidth = this.containerWidth;
34565 this.getContainerWidth();
34566 return previousWidth !== this.containerWidth;
34581 * @class Roo.bootstrap.MasonryBrick
34582 * @extends Roo.bootstrap.Component
34583 * Bootstrap MasonryBrick class
34586 * Create a new MasonryBrick
34587 * @param {Object} config The config object
34590 Roo.bootstrap.MasonryBrick = function(config){
34592 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34594 Roo.bootstrap.MasonryBrick.register(this);
34600 * When a MasonryBrick is clcik
34601 * @param {Roo.bootstrap.MasonryBrick} this
34602 * @param {Roo.EventObject} e
34608 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34611 * @cfg {String} title
34615 * @cfg {String} html
34619 * @cfg {String} bgimage
34623 * @cfg {String} videourl
34627 * @cfg {String} cls
34631 * @cfg {String} href
34635 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34640 * @cfg {String} placetitle (center|bottom)
34645 * @cfg {Boolean} isFitContainer defalut true
34647 isFitContainer : true,
34650 * @cfg {Boolean} preventDefault defalut false
34652 preventDefault : false,
34655 * @cfg {Boolean} inverse defalut false
34657 maskInverse : false,
34659 getAutoCreate : function()
34661 if(!this.isFitContainer){
34662 return this.getSplitAutoCreate();
34665 var cls = 'masonry-brick masonry-brick-full';
34667 if(this.href.length){
34668 cls += ' masonry-brick-link';
34671 if(this.bgimage.length){
34672 cls += ' masonry-brick-image';
34675 if(this.maskInverse){
34676 cls += ' mask-inverse';
34679 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34680 cls += ' enable-mask';
34684 cls += ' masonry-' + this.size + '-brick';
34687 if(this.placetitle.length){
34689 switch (this.placetitle) {
34691 cls += ' masonry-center-title';
34694 cls += ' masonry-bottom-title';
34701 if(!this.html.length && !this.bgimage.length){
34702 cls += ' masonry-center-title';
34705 if(!this.html.length && this.bgimage.length){
34706 cls += ' masonry-bottom-title';
34711 cls += ' ' + this.cls;
34715 tag: (this.href.length) ? 'a' : 'div',
34720 cls: 'masonry-brick-mask'
34724 cls: 'masonry-brick-paragraph',
34730 if(this.href.length){
34731 cfg.href = this.href;
34734 var cn = cfg.cn[1].cn;
34736 if(this.title.length){
34739 cls: 'masonry-brick-title',
34744 if(this.html.length){
34747 cls: 'masonry-brick-text',
34752 if (!this.title.length && !this.html.length) {
34753 cfg.cn[1].cls += ' hide';
34756 if(this.bgimage.length){
34759 cls: 'masonry-brick-image-view',
34764 if(this.videourl.length){
34765 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34766 // youtube support only?
34769 cls: 'masonry-brick-image-view',
34772 allowfullscreen : true
34780 getSplitAutoCreate : function()
34782 var cls = 'masonry-brick masonry-brick-split';
34784 if(this.href.length){
34785 cls += ' masonry-brick-link';
34788 if(this.bgimage.length){
34789 cls += ' masonry-brick-image';
34793 cls += ' masonry-' + this.size + '-brick';
34796 switch (this.placetitle) {
34798 cls += ' masonry-center-title';
34801 cls += ' masonry-bottom-title';
34804 if(!this.bgimage.length){
34805 cls += ' masonry-center-title';
34808 if(this.bgimage.length){
34809 cls += ' masonry-bottom-title';
34815 cls += ' ' + this.cls;
34819 tag: (this.href.length) ? 'a' : 'div',
34824 cls: 'masonry-brick-split-head',
34828 cls: 'masonry-brick-paragraph',
34835 cls: 'masonry-brick-split-body',
34841 if(this.href.length){
34842 cfg.href = this.href;
34845 if(this.title.length){
34846 cfg.cn[0].cn[0].cn.push({
34848 cls: 'masonry-brick-title',
34853 if(this.html.length){
34854 cfg.cn[1].cn.push({
34856 cls: 'masonry-brick-text',
34861 if(this.bgimage.length){
34862 cfg.cn[0].cn.push({
34864 cls: 'masonry-brick-image-view',
34869 if(this.videourl.length){
34870 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34871 // youtube support only?
34872 cfg.cn[0].cn.cn.push({
34874 cls: 'masonry-brick-image-view',
34877 allowfullscreen : true
34884 initEvents: function()
34886 switch (this.size) {
34919 this.el.on('touchstart', this.onTouchStart, this);
34920 this.el.on('touchmove', this.onTouchMove, this);
34921 this.el.on('touchend', this.onTouchEnd, this);
34922 this.el.on('contextmenu', this.onContextMenu, this);
34924 this.el.on('mouseenter' ,this.enter, this);
34925 this.el.on('mouseleave', this.leave, this);
34926 this.el.on('click', this.onClick, this);
34929 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34930 this.parent().bricks.push(this);
34935 onClick: function(e, el)
34937 var time = this.endTimer - this.startTimer;
34938 // Roo.log(e.preventDefault());
34941 e.preventDefault();
34946 if(!this.preventDefault){
34950 e.preventDefault();
34952 if (this.activeClass != '') {
34953 this.selectBrick();
34956 this.fireEvent('click', this, e);
34959 enter: function(e, el)
34961 e.preventDefault();
34963 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34967 if(this.bgimage.length && this.html.length){
34968 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34972 leave: function(e, el)
34974 e.preventDefault();
34976 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34980 if(this.bgimage.length && this.html.length){
34981 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34985 onTouchStart: function(e, el)
34987 // e.preventDefault();
34989 this.touchmoved = false;
34991 if(!this.isFitContainer){
34995 if(!this.bgimage.length || !this.html.length){
34999 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35001 this.timer = new Date().getTime();
35005 onTouchMove: function(e, el)
35007 this.touchmoved = true;
35010 onContextMenu : function(e,el)
35012 e.preventDefault();
35013 e.stopPropagation();
35017 onTouchEnd: function(e, el)
35019 // e.preventDefault();
35021 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35028 if(!this.bgimage.length || !this.html.length){
35030 if(this.href.length){
35031 window.location.href = this.href;
35037 if(!this.isFitContainer){
35041 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35043 window.location.href = this.href;
35046 //selection on single brick only
35047 selectBrick : function() {
35049 if (!this.parentId) {
35053 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35054 var index = m.selectedBrick.indexOf(this.id);
35057 m.selectedBrick.splice(index,1);
35058 this.el.removeClass(this.activeClass);
35062 for(var i = 0; i < m.selectedBrick.length; i++) {
35063 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35064 b.el.removeClass(b.activeClass);
35067 m.selectedBrick = [];
35069 m.selectedBrick.push(this.id);
35070 this.el.addClass(this.activeClass);
35074 isSelected : function(){
35075 return this.el.hasClass(this.activeClass);
35080 Roo.apply(Roo.bootstrap.MasonryBrick, {
35083 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35085 * register a Masonry Brick
35086 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35089 register : function(brick)
35091 //this.groups[brick.id] = brick;
35092 this.groups.add(brick.id, brick);
35095 * fetch a masonry brick based on the masonry brick ID
35096 * @param {string} the masonry brick to add
35097 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35100 get: function(brick_id)
35102 // if (typeof(this.groups[brick_id]) == 'undefined') {
35105 // return this.groups[brick_id] ;
35107 if(this.groups.key(brick_id)) {
35108 return this.groups.key(brick_id);
35126 * @class Roo.bootstrap.Brick
35127 * @extends Roo.bootstrap.Component
35128 * Bootstrap Brick class
35131 * Create a new Brick
35132 * @param {Object} config The config object
35135 Roo.bootstrap.Brick = function(config){
35136 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35142 * When a Brick is click
35143 * @param {Roo.bootstrap.Brick} this
35144 * @param {Roo.EventObject} e
35150 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35153 * @cfg {String} title
35157 * @cfg {String} html
35161 * @cfg {String} bgimage
35165 * @cfg {String} cls
35169 * @cfg {String} href
35173 * @cfg {String} video
35177 * @cfg {Boolean} square
35181 getAutoCreate : function()
35183 var cls = 'roo-brick';
35185 if(this.href.length){
35186 cls += ' roo-brick-link';
35189 if(this.bgimage.length){
35190 cls += ' roo-brick-image';
35193 if(!this.html.length && !this.bgimage.length){
35194 cls += ' roo-brick-center-title';
35197 if(!this.html.length && this.bgimage.length){
35198 cls += ' roo-brick-bottom-title';
35202 cls += ' ' + this.cls;
35206 tag: (this.href.length) ? 'a' : 'div',
35211 cls: 'roo-brick-paragraph',
35217 if(this.href.length){
35218 cfg.href = this.href;
35221 var cn = cfg.cn[0].cn;
35223 if(this.title.length){
35226 cls: 'roo-brick-title',
35231 if(this.html.length){
35234 cls: 'roo-brick-text',
35241 if(this.bgimage.length){
35244 cls: 'roo-brick-image-view',
35252 initEvents: function()
35254 if(this.title.length || this.html.length){
35255 this.el.on('mouseenter' ,this.enter, this);
35256 this.el.on('mouseleave', this.leave, this);
35259 Roo.EventManager.onWindowResize(this.resize, this);
35261 if(this.bgimage.length){
35262 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35263 this.imageEl.on('load', this.onImageLoad, this);
35270 onImageLoad : function()
35275 resize : function()
35277 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35279 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35281 if(this.bgimage.length){
35282 var image = this.el.select('.roo-brick-image-view', true).first();
35284 image.setWidth(paragraph.getWidth());
35287 image.setHeight(paragraph.getWidth());
35290 this.el.setHeight(image.getHeight());
35291 paragraph.setHeight(image.getHeight());
35297 enter: function(e, el)
35299 e.preventDefault();
35301 if(this.bgimage.length){
35302 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35303 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35307 leave: function(e, el)
35309 e.preventDefault();
35311 if(this.bgimage.length){
35312 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35313 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35328 * @class Roo.bootstrap.NumberField
35329 * @extends Roo.bootstrap.Input
35330 * Bootstrap NumberField class
35336 * Create a new NumberField
35337 * @param {Object} config The config object
35340 Roo.bootstrap.NumberField = function(config){
35341 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35344 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35347 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35349 allowDecimals : true,
35351 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35353 decimalSeparator : ".",
35355 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35357 decimalPrecision : 2,
35359 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35361 allowNegative : true,
35364 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35368 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35370 minValue : Number.NEGATIVE_INFINITY,
35372 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35374 maxValue : Number.MAX_VALUE,
35376 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35378 minText : "The minimum value for this field is {0}",
35380 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35382 maxText : "The maximum value for this field is {0}",
35384 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35385 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35387 nanText : "{0} is not a valid number",
35389 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35391 thousandsDelimiter : false,
35393 * @cfg {String} valueAlign alignment of value
35395 valueAlign : "left",
35397 getAutoCreate : function()
35399 var hiddenInput = {
35403 cls: 'hidden-number-input'
35407 hiddenInput.name = this.name;
35412 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35414 this.name = hiddenInput.name;
35416 if(cfg.cn.length > 0) {
35417 cfg.cn.push(hiddenInput);
35424 initEvents : function()
35426 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35428 var allowed = "0123456789";
35430 if(this.allowDecimals){
35431 allowed += this.decimalSeparator;
35434 if(this.allowNegative){
35438 if(this.thousandsDelimiter) {
35442 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35444 var keyPress = function(e){
35446 var k = e.getKey();
35448 var c = e.getCharCode();
35451 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35452 allowed.indexOf(String.fromCharCode(c)) === -1
35458 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35462 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35467 this.el.on("keypress", keyPress, this);
35470 validateValue : function(value)
35473 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35477 var num = this.parseValue(value);
35480 this.markInvalid(String.format(this.nanText, value));
35484 if(num < this.minValue){
35485 this.markInvalid(String.format(this.minText, this.minValue));
35489 if(num > this.maxValue){
35490 this.markInvalid(String.format(this.maxText, this.maxValue));
35497 getValue : function()
35499 var v = this.hiddenEl().getValue();
35501 return this.fixPrecision(this.parseValue(v));
35504 parseValue : function(value)
35506 if(this.thousandsDelimiter) {
35508 r = new RegExp(",", "g");
35509 value = value.replace(r, "");
35512 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35513 return isNaN(value) ? '' : value;
35516 fixPrecision : function(value)
35518 if(this.thousandsDelimiter) {
35520 r = new RegExp(",", "g");
35521 value = value.replace(r, "");
35524 var nan = isNaN(value);
35526 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35527 return nan ? '' : value;
35529 return parseFloat(value).toFixed(this.decimalPrecision);
35532 setValue : function(v)
35534 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35540 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35542 this.inputEl().dom.value = (v == '') ? '' :
35543 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35545 if(!this.allowZero && v === '0') {
35546 this.hiddenEl().dom.value = '';
35547 this.inputEl().dom.value = '';
35554 decimalPrecisionFcn : function(v)
35556 return Math.floor(v);
35559 beforeBlur : function()
35561 var v = this.parseValue(this.getRawValue());
35563 if(v || v === 0 || v === ''){
35568 hiddenEl : function()
35570 return this.el.select('input.hidden-number-input',true).first();
35582 * @class Roo.bootstrap.DocumentSlider
35583 * @extends Roo.bootstrap.Component
35584 * Bootstrap DocumentSlider class
35587 * Create a new DocumentViewer
35588 * @param {Object} config The config object
35591 Roo.bootstrap.DocumentSlider = function(config){
35592 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35599 * Fire after initEvent
35600 * @param {Roo.bootstrap.DocumentSlider} this
35605 * Fire after update
35606 * @param {Roo.bootstrap.DocumentSlider} this
35612 * @param {Roo.bootstrap.DocumentSlider} this
35618 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35624 getAutoCreate : function()
35628 cls : 'roo-document-slider',
35632 cls : 'roo-document-slider-header',
35636 cls : 'roo-document-slider-header-title'
35642 cls : 'roo-document-slider-body',
35646 cls : 'roo-document-slider-prev',
35650 cls : 'fa fa-chevron-left'
35656 cls : 'roo-document-slider-thumb',
35660 cls : 'roo-document-slider-image'
35666 cls : 'roo-document-slider-next',
35670 cls : 'fa fa-chevron-right'
35682 initEvents : function()
35684 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35685 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35687 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35688 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35690 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35691 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35693 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35694 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35696 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35697 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35699 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35700 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35702 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35703 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35705 this.thumbEl.on('click', this.onClick, this);
35707 this.prevIndicator.on('click', this.prev, this);
35709 this.nextIndicator.on('click', this.next, this);
35713 initial : function()
35715 if(this.files.length){
35716 this.indicator = 1;
35720 this.fireEvent('initial', this);
35723 update : function()
35725 this.imageEl.attr('src', this.files[this.indicator - 1]);
35727 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35729 this.prevIndicator.show();
35731 if(this.indicator == 1){
35732 this.prevIndicator.hide();
35735 this.nextIndicator.show();
35737 if(this.indicator == this.files.length){
35738 this.nextIndicator.hide();
35741 this.thumbEl.scrollTo('top');
35743 this.fireEvent('update', this);
35746 onClick : function(e)
35748 e.preventDefault();
35750 this.fireEvent('click', this);
35755 e.preventDefault();
35757 this.indicator = Math.max(1, this.indicator - 1);
35764 e.preventDefault();
35766 this.indicator = Math.min(this.files.length, this.indicator + 1);
35780 * @class Roo.bootstrap.RadioSet
35781 * @extends Roo.bootstrap.Input
35782 * Bootstrap RadioSet class
35783 * @cfg {String} indicatorpos (left|right) default left
35784 * @cfg {Boolean} inline (true|false) inline the element (default true)
35785 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35787 * Create a new RadioSet
35788 * @param {Object} config The config object
35791 Roo.bootstrap.RadioSet = function(config){
35793 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35797 Roo.bootstrap.RadioSet.register(this);
35802 * Fires when the element is checked or unchecked.
35803 * @param {Roo.bootstrap.RadioSet} this This radio
35804 * @param {Roo.bootstrap.Radio} item The checked item
35809 * Fires when the element is click.
35810 * @param {Roo.bootstrap.RadioSet} this This radio set
35811 * @param {Roo.bootstrap.Radio} item The checked item
35812 * @param {Roo.EventObject} e The event object
35819 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35827 indicatorpos : 'left',
35829 getAutoCreate : function()
35833 cls : 'roo-radio-set-label',
35837 html : this.fieldLabel
35841 if (Roo.bootstrap.version == 3) {
35844 if(this.indicatorpos == 'left'){
35847 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35848 tooltip : 'This field is required'
35853 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35854 tooltip : 'This field is required'
35860 cls : 'roo-radio-set-items'
35863 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35865 if (align === 'left' && this.fieldLabel.length) {
35868 cls : "roo-radio-set-right",
35874 if(this.labelWidth > 12){
35875 label.style = "width: " + this.labelWidth + 'px';
35878 if(this.labelWidth < 13 && this.labelmd == 0){
35879 this.labelmd = this.labelWidth;
35882 if(this.labellg > 0){
35883 label.cls += ' col-lg-' + this.labellg;
35884 items.cls += ' col-lg-' + (12 - this.labellg);
35887 if(this.labelmd > 0){
35888 label.cls += ' col-md-' + this.labelmd;
35889 items.cls += ' col-md-' + (12 - this.labelmd);
35892 if(this.labelsm > 0){
35893 label.cls += ' col-sm-' + this.labelsm;
35894 items.cls += ' col-sm-' + (12 - this.labelsm);
35897 if(this.labelxs > 0){
35898 label.cls += ' col-xs-' + this.labelxs;
35899 items.cls += ' col-xs-' + (12 - this.labelxs);
35905 cls : 'roo-radio-set',
35909 cls : 'roo-radio-set-input',
35912 value : this.value ? this.value : ''
35919 if(this.weight.length){
35920 cfg.cls += ' roo-radio-' + this.weight;
35924 cfg.cls += ' roo-radio-set-inline';
35928 ['xs','sm','md','lg'].map(function(size){
35929 if (settings[size]) {
35930 cfg.cls += ' col-' + size + '-' + settings[size];
35938 initEvents : function()
35940 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35941 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35943 if(!this.fieldLabel.length){
35944 this.labelEl.hide();
35947 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35948 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35950 this.indicator = this.indicatorEl();
35952 if(this.indicator){
35953 this.indicator.addClass('invisible');
35956 this.originalValue = this.getValue();
35960 inputEl: function ()
35962 return this.el.select('.roo-radio-set-input', true).first();
35965 getChildContainer : function()
35967 return this.itemsEl;
35970 register : function(item)
35972 this.radioes.push(item);
35976 validate : function()
35978 if(this.getVisibilityEl().hasClass('hidden')){
35984 Roo.each(this.radioes, function(i){
35993 if(this.allowBlank) {
35997 if(this.disabled || valid){
36002 this.markInvalid();
36007 markValid : function()
36009 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36010 this.indicatorEl().removeClass('visible');
36011 this.indicatorEl().addClass('invisible');
36015 if (Roo.bootstrap.version == 3) {
36016 this.el.removeClass([this.invalidClass, this.validClass]);
36017 this.el.addClass(this.validClass);
36019 this.el.removeClass(['is-invalid','is-valid']);
36020 this.el.addClass(['is-valid']);
36022 this.fireEvent('valid', this);
36025 markInvalid : function(msg)
36027 if(this.allowBlank || this.disabled){
36031 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36032 this.indicatorEl().removeClass('invisible');
36033 this.indicatorEl().addClass('visible');
36035 if (Roo.bootstrap.version == 3) {
36036 this.el.removeClass([this.invalidClass, this.validClass]);
36037 this.el.addClass(this.invalidClass);
36039 this.el.removeClass(['is-invalid','is-valid']);
36040 this.el.addClass(['is-invalid']);
36043 this.fireEvent('invalid', this, msg);
36047 setValue : function(v, suppressEvent)
36049 if(this.value === v){
36056 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36059 Roo.each(this.radioes, function(i){
36061 i.el.removeClass('checked');
36064 Roo.each(this.radioes, function(i){
36066 if(i.value === v || i.value.toString() === v.toString()){
36068 i.el.addClass('checked');
36070 if(suppressEvent !== true){
36071 this.fireEvent('check', this, i);
36082 clearInvalid : function(){
36084 if(!this.el || this.preventMark){
36088 this.el.removeClass([this.invalidClass]);
36090 this.fireEvent('valid', this);
36095 Roo.apply(Roo.bootstrap.RadioSet, {
36099 register : function(set)
36101 this.groups[set.name] = set;
36104 get: function(name)
36106 if (typeof(this.groups[name]) == 'undefined') {
36110 return this.groups[name] ;
36116 * Ext JS Library 1.1.1
36117 * Copyright(c) 2006-2007, Ext JS, LLC.
36119 * Originally Released Under LGPL - original licence link has changed is not relivant.
36122 * <script type="text/javascript">
36127 * @class Roo.bootstrap.SplitBar
36128 * @extends Roo.util.Observable
36129 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36133 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36134 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36135 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36136 split.minSize = 100;
36137 split.maxSize = 600;
36138 split.animate = true;
36139 split.on('moved', splitterMoved);
36142 * Create a new SplitBar
36143 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36144 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36145 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36146 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36147 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36148 position of the SplitBar).
36150 Roo.bootstrap.SplitBar = function(cfg){
36155 // dragElement : elm
36156 // resizingElement: el,
36158 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36159 // placement : Roo.bootstrap.SplitBar.LEFT ,
36160 // existingProxy ???
36163 this.el = Roo.get(cfg.dragElement, true);
36164 this.el.dom.unselectable = "on";
36166 this.resizingEl = Roo.get(cfg.resizingElement, true);
36170 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36171 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36174 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36177 * The minimum size of the resizing element. (Defaults to 0)
36183 * The maximum size of the resizing element. (Defaults to 2000)
36186 this.maxSize = 2000;
36189 * Whether to animate the transition to the new size
36192 this.animate = false;
36195 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36198 this.useShim = false;
36203 if(!cfg.existingProxy){
36205 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36207 this.proxy = Roo.get(cfg.existingProxy).dom;
36210 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36213 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36216 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36219 this.dragSpecs = {};
36222 * @private The adapter to use to positon and resize elements
36224 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36225 this.adapter.init(this);
36227 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36229 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36230 this.el.addClass("roo-splitbar-h");
36233 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36234 this.el.addClass("roo-splitbar-v");
36240 * Fires when the splitter is moved (alias for {@link #event-moved})
36241 * @param {Roo.bootstrap.SplitBar} this
36242 * @param {Number} newSize the new width or height
36247 * Fires when the splitter is moved
36248 * @param {Roo.bootstrap.SplitBar} this
36249 * @param {Number} newSize the new width or height
36253 * @event beforeresize
36254 * Fires before the splitter is dragged
36255 * @param {Roo.bootstrap.SplitBar} this
36257 "beforeresize" : true,
36259 "beforeapply" : true
36262 Roo.util.Observable.call(this);
36265 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36266 onStartProxyDrag : function(x, y){
36267 this.fireEvent("beforeresize", this);
36269 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36271 o.enableDisplayMode("block");
36272 // all splitbars share the same overlay
36273 Roo.bootstrap.SplitBar.prototype.overlay = o;
36275 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36276 this.overlay.show();
36277 Roo.get(this.proxy).setDisplayed("block");
36278 var size = this.adapter.getElementSize(this);
36279 this.activeMinSize = this.getMinimumSize();;
36280 this.activeMaxSize = this.getMaximumSize();;
36281 var c1 = size - this.activeMinSize;
36282 var c2 = Math.max(this.activeMaxSize - size, 0);
36283 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36284 this.dd.resetConstraints();
36285 this.dd.setXConstraint(
36286 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36287 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36289 this.dd.setYConstraint(0, 0);
36291 this.dd.resetConstraints();
36292 this.dd.setXConstraint(0, 0);
36293 this.dd.setYConstraint(
36294 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36295 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36298 this.dragSpecs.startSize = size;
36299 this.dragSpecs.startPoint = [x, y];
36300 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36304 * @private Called after the drag operation by the DDProxy
36306 onEndProxyDrag : function(e){
36307 Roo.get(this.proxy).setDisplayed(false);
36308 var endPoint = Roo.lib.Event.getXY(e);
36310 this.overlay.hide();
36313 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36314 newSize = this.dragSpecs.startSize +
36315 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36316 endPoint[0] - this.dragSpecs.startPoint[0] :
36317 this.dragSpecs.startPoint[0] - endPoint[0]
36320 newSize = this.dragSpecs.startSize +
36321 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36322 endPoint[1] - this.dragSpecs.startPoint[1] :
36323 this.dragSpecs.startPoint[1] - endPoint[1]
36326 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36327 if(newSize != this.dragSpecs.startSize){
36328 if(this.fireEvent('beforeapply', this, newSize) !== false){
36329 this.adapter.setElementSize(this, newSize);
36330 this.fireEvent("moved", this, newSize);
36331 this.fireEvent("resize", this, newSize);
36337 * Get the adapter this SplitBar uses
36338 * @return The adapter object
36340 getAdapter : function(){
36341 return this.adapter;
36345 * Set the adapter this SplitBar uses
36346 * @param {Object} adapter A SplitBar adapter object
36348 setAdapter : function(adapter){
36349 this.adapter = adapter;
36350 this.adapter.init(this);
36354 * Gets the minimum size for the resizing element
36355 * @return {Number} The minimum size
36357 getMinimumSize : function(){
36358 return this.minSize;
36362 * Sets the minimum size for the resizing element
36363 * @param {Number} minSize The minimum size
36365 setMinimumSize : function(minSize){
36366 this.minSize = minSize;
36370 * Gets the maximum size for the resizing element
36371 * @return {Number} The maximum size
36373 getMaximumSize : function(){
36374 return this.maxSize;
36378 * Sets the maximum size for the resizing element
36379 * @param {Number} maxSize The maximum size
36381 setMaximumSize : function(maxSize){
36382 this.maxSize = maxSize;
36386 * Sets the initialize size for the resizing element
36387 * @param {Number} size The initial size
36389 setCurrentSize : function(size){
36390 var oldAnimate = this.animate;
36391 this.animate = false;
36392 this.adapter.setElementSize(this, size);
36393 this.animate = oldAnimate;
36397 * Destroy this splitbar.
36398 * @param {Boolean} removeEl True to remove the element
36400 destroy : function(removeEl){
36402 this.shim.remove();
36405 this.proxy.parentNode.removeChild(this.proxy);
36413 * @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.
36415 Roo.bootstrap.SplitBar.createProxy = function(dir){
36416 var proxy = new Roo.Element(document.createElement("div"));
36417 proxy.unselectable();
36418 var cls = 'roo-splitbar-proxy';
36419 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36420 document.body.appendChild(proxy.dom);
36425 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36426 * Default Adapter. It assumes the splitter and resizing element are not positioned
36427 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36429 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36432 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36433 // do nothing for now
36434 init : function(s){
36438 * Called before drag operations to get the current size of the resizing element.
36439 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36441 getElementSize : function(s){
36442 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36443 return s.resizingEl.getWidth();
36445 return s.resizingEl.getHeight();
36450 * Called after drag operations to set the size of the resizing element.
36451 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36452 * @param {Number} newSize The new size to set
36453 * @param {Function} onComplete A function to be invoked when resizing is complete
36455 setElementSize : function(s, newSize, onComplete){
36456 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36458 s.resizingEl.setWidth(newSize);
36460 onComplete(s, newSize);
36463 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36468 s.resizingEl.setHeight(newSize);
36470 onComplete(s, newSize);
36473 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36480 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36481 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36482 * Adapter that moves the splitter element to align with the resized sizing element.
36483 * Used with an absolute positioned SplitBar.
36484 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36485 * document.body, make sure you assign an id to the body element.
36487 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36488 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36489 this.container = Roo.get(container);
36492 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36493 init : function(s){
36494 this.basic.init(s);
36497 getElementSize : function(s){
36498 return this.basic.getElementSize(s);
36501 setElementSize : function(s, newSize, onComplete){
36502 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36505 moveSplitter : function(s){
36506 var yes = Roo.bootstrap.SplitBar;
36507 switch(s.placement){
36509 s.el.setX(s.resizingEl.getRight());
36512 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36515 s.el.setY(s.resizingEl.getBottom());
36518 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36525 * Orientation constant - Create a vertical SplitBar
36529 Roo.bootstrap.SplitBar.VERTICAL = 1;
36532 * Orientation constant - Create a horizontal SplitBar
36536 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36539 * Placement constant - The resizing element is to the left of the splitter element
36543 Roo.bootstrap.SplitBar.LEFT = 1;
36546 * Placement constant - The resizing element is to the right of the splitter element
36550 Roo.bootstrap.SplitBar.RIGHT = 2;
36553 * Placement constant - The resizing element is positioned above the splitter element
36557 Roo.bootstrap.SplitBar.TOP = 3;
36560 * Placement constant - The resizing element is positioned under splitter element
36564 Roo.bootstrap.SplitBar.BOTTOM = 4;
36565 Roo.namespace("Roo.bootstrap.layout");/*
36567 * Ext JS Library 1.1.1
36568 * Copyright(c) 2006-2007, Ext JS, LLC.
36570 * Originally Released Under LGPL - original licence link has changed is not relivant.
36573 * <script type="text/javascript">
36577 * @class Roo.bootstrap.layout.Manager
36578 * @extends Roo.bootstrap.Component
36579 * Base class for layout managers.
36581 Roo.bootstrap.layout.Manager = function(config)
36583 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36589 /** false to disable window resize monitoring @type Boolean */
36590 this.monitorWindowResize = true;
36595 * Fires when a layout is performed.
36596 * @param {Roo.LayoutManager} this
36600 * @event regionresized
36601 * Fires when the user resizes a region.
36602 * @param {Roo.LayoutRegion} region The resized region
36603 * @param {Number} newSize The new size (width for east/west, height for north/south)
36605 "regionresized" : true,
36607 * @event regioncollapsed
36608 * Fires when a region is collapsed.
36609 * @param {Roo.LayoutRegion} region The collapsed region
36611 "regioncollapsed" : true,
36613 * @event regionexpanded
36614 * Fires when a region is expanded.
36615 * @param {Roo.LayoutRegion} region The expanded region
36617 "regionexpanded" : true
36619 this.updating = false;
36622 this.el = Roo.get(config.el);
36628 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36633 monitorWindowResize : true,
36639 onRender : function(ct, position)
36642 this.el = Roo.get(ct);
36645 //this.fireEvent('render',this);
36649 initEvents: function()
36653 // ie scrollbar fix
36654 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36655 document.body.scroll = "no";
36656 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36657 this.el.position('relative');
36659 this.id = this.el.id;
36660 this.el.addClass("roo-layout-container");
36661 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36662 if(this.el.dom != document.body ) {
36663 this.el.on('resize', this.layout,this);
36664 this.el.on('show', this.layout,this);
36670 * Returns true if this layout is currently being updated
36671 * @return {Boolean}
36673 isUpdating : function(){
36674 return this.updating;
36678 * Suspend the LayoutManager from doing auto-layouts while
36679 * making multiple add or remove calls
36681 beginUpdate : function(){
36682 this.updating = true;
36686 * Restore auto-layouts and optionally disable the manager from performing a layout
36687 * @param {Boolean} noLayout true to disable a layout update
36689 endUpdate : function(noLayout){
36690 this.updating = false;
36696 layout: function(){
36700 onRegionResized : function(region, newSize){
36701 this.fireEvent("regionresized", region, newSize);
36705 onRegionCollapsed : function(region){
36706 this.fireEvent("regioncollapsed", region);
36709 onRegionExpanded : function(region){
36710 this.fireEvent("regionexpanded", region);
36714 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36715 * performs box-model adjustments.
36716 * @return {Object} The size as an object {width: (the width), height: (the height)}
36718 getViewSize : function()
36721 if(this.el.dom != document.body){
36722 size = this.el.getSize();
36724 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36726 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36727 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36732 * Returns the Element this layout is bound to.
36733 * @return {Roo.Element}
36735 getEl : function(){
36740 * Returns the specified region.
36741 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36742 * @return {Roo.LayoutRegion}
36744 getRegion : function(target){
36745 return this.regions[target.toLowerCase()];
36748 onWindowResize : function(){
36749 if(this.monitorWindowResize){
36756 * Ext JS Library 1.1.1
36757 * Copyright(c) 2006-2007, Ext JS, LLC.
36759 * Originally Released Under LGPL - original licence link has changed is not relivant.
36762 * <script type="text/javascript">
36765 * @class Roo.bootstrap.layout.Border
36766 * @extends Roo.bootstrap.layout.Manager
36767 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36768 * please see: examples/bootstrap/nested.html<br><br>
36770 <b>The container the layout is rendered into can be either the body element or any other element.
36771 If it is not the body element, the container needs to either be an absolute positioned element,
36772 or you will need to add "position:relative" to the css of the container. You will also need to specify
36773 the container size if it is not the body element.</b>
36776 * Create a new Border
36777 * @param {Object} config Configuration options
36779 Roo.bootstrap.layout.Border = function(config){
36780 config = config || {};
36781 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36785 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36786 if(config[region]){
36787 config[region].region = region;
36788 this.addRegion(config[region]);
36794 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36796 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36798 parent : false, // this might point to a 'nest' or a ???
36801 * Creates and adds a new region if it doesn't already exist.
36802 * @param {String} target The target region key (north, south, east, west or center).
36803 * @param {Object} config The regions config object
36804 * @return {BorderLayoutRegion} The new region
36806 addRegion : function(config)
36808 if(!this.regions[config.region]){
36809 var r = this.factory(config);
36810 this.bindRegion(r);
36812 return this.regions[config.region];
36816 bindRegion : function(r){
36817 this.regions[r.config.region] = r;
36819 r.on("visibilitychange", this.layout, this);
36820 r.on("paneladded", this.layout, this);
36821 r.on("panelremoved", this.layout, this);
36822 r.on("invalidated", this.layout, this);
36823 r.on("resized", this.onRegionResized, this);
36824 r.on("collapsed", this.onRegionCollapsed, this);
36825 r.on("expanded", this.onRegionExpanded, this);
36829 * Performs a layout update.
36831 layout : function()
36833 if(this.updating) {
36837 // render all the rebions if they have not been done alreayd?
36838 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36839 if(this.regions[region] && !this.regions[region].bodyEl){
36840 this.regions[region].onRender(this.el)
36844 var size = this.getViewSize();
36845 var w = size.width;
36846 var h = size.height;
36851 //var x = 0, y = 0;
36853 var rs = this.regions;
36854 var north = rs["north"];
36855 var south = rs["south"];
36856 var west = rs["west"];
36857 var east = rs["east"];
36858 var center = rs["center"];
36859 //if(this.hideOnLayout){ // not supported anymore
36860 //c.el.setStyle("display", "none");
36862 if(north && north.isVisible()){
36863 var b = north.getBox();
36864 var m = north.getMargins();
36865 b.width = w - (m.left+m.right);
36868 centerY = b.height + b.y + m.bottom;
36869 centerH -= centerY;
36870 north.updateBox(this.safeBox(b));
36872 if(south && south.isVisible()){
36873 var b = south.getBox();
36874 var m = south.getMargins();
36875 b.width = w - (m.left+m.right);
36877 var totalHeight = (b.height + m.top + m.bottom);
36878 b.y = h - totalHeight + m.top;
36879 centerH -= totalHeight;
36880 south.updateBox(this.safeBox(b));
36882 if(west && west.isVisible()){
36883 var b = west.getBox();
36884 var m = west.getMargins();
36885 b.height = centerH - (m.top+m.bottom);
36887 b.y = centerY + m.top;
36888 var totalWidth = (b.width + m.left + m.right);
36889 centerX += totalWidth;
36890 centerW -= totalWidth;
36891 west.updateBox(this.safeBox(b));
36893 if(east && east.isVisible()){
36894 var b = east.getBox();
36895 var m = east.getMargins();
36896 b.height = centerH - (m.top+m.bottom);
36897 var totalWidth = (b.width + m.left + m.right);
36898 b.x = w - totalWidth + m.left;
36899 b.y = centerY + m.top;
36900 centerW -= totalWidth;
36901 east.updateBox(this.safeBox(b));
36904 var m = center.getMargins();
36906 x: centerX + m.left,
36907 y: centerY + m.top,
36908 width: centerW - (m.left+m.right),
36909 height: centerH - (m.top+m.bottom)
36911 //if(this.hideOnLayout){
36912 //center.el.setStyle("display", "block");
36914 center.updateBox(this.safeBox(centerBox));
36917 this.fireEvent("layout", this);
36921 safeBox : function(box){
36922 box.width = Math.max(0, box.width);
36923 box.height = Math.max(0, box.height);
36928 * Adds a ContentPanel (or subclass) to this layout.
36929 * @param {String} target The target region key (north, south, east, west or center).
36930 * @param {Roo.ContentPanel} panel The panel to add
36931 * @return {Roo.ContentPanel} The added panel
36933 add : function(target, panel){
36935 target = target.toLowerCase();
36936 return this.regions[target].add(panel);
36940 * Remove a ContentPanel (or subclass) to this layout.
36941 * @param {String} target The target region key (north, south, east, west or center).
36942 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36943 * @return {Roo.ContentPanel} The removed panel
36945 remove : function(target, panel){
36946 target = target.toLowerCase();
36947 return this.regions[target].remove(panel);
36951 * Searches all regions for a panel with the specified id
36952 * @param {String} panelId
36953 * @return {Roo.ContentPanel} The panel or null if it wasn't found
36955 findPanel : function(panelId){
36956 var rs = this.regions;
36957 for(var target in rs){
36958 if(typeof rs[target] != "function"){
36959 var p = rs[target].getPanel(panelId);
36969 * Searches all regions for a panel with the specified id and activates (shows) it.
36970 * @param {String/ContentPanel} panelId The panels id or the panel itself
36971 * @return {Roo.ContentPanel} The shown panel or null
36973 showPanel : function(panelId) {
36974 var rs = this.regions;
36975 for(var target in rs){
36976 var r = rs[target];
36977 if(typeof r != "function"){
36978 if(r.hasPanel(panelId)){
36979 return r.showPanel(panelId);
36987 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36988 * @param {Roo.state.Provider} provider (optional) An alternate state provider
36991 restoreState : function(provider){
36993 provider = Roo.state.Manager;
36995 var sm = new Roo.LayoutStateManager();
36996 sm.init(this, provider);
37002 * Adds a xtype elements to the layout.
37006 xtype : 'ContentPanel',
37013 xtype : 'NestedLayoutPanel',
37019 items : [ ... list of content panels or nested layout panels.. ]
37023 * @param {Object} cfg Xtype definition of item to add.
37025 addxtype : function(cfg)
37027 // basically accepts a pannel...
37028 // can accept a layout region..!?!?
37029 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37032 // theory? children can only be panels??
37034 //if (!cfg.xtype.match(/Panel$/)) {
37039 if (typeof(cfg.region) == 'undefined') {
37040 Roo.log("Failed to add Panel, region was not set");
37044 var region = cfg.region;
37050 xitems = cfg.items;
37055 if ( region == 'center') {
37056 Roo.log("Center: " + cfg.title);
37062 case 'Content': // ContentPanel (el, cfg)
37063 case 'Scroll': // ContentPanel (el, cfg)
37065 cfg.autoCreate = cfg.autoCreate || true;
37066 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37068 // var el = this.el.createChild();
37069 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37072 this.add(region, ret);
37076 case 'TreePanel': // our new panel!
37077 cfg.el = this.el.createChild();
37078 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37079 this.add(region, ret);
37084 // create a new Layout (which is a Border Layout...
37086 var clayout = cfg.layout;
37087 clayout.el = this.el.createChild();
37088 clayout.items = clayout.items || [];
37092 // replace this exitems with the clayout ones..
37093 xitems = clayout.items;
37095 // force background off if it's in center...
37096 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37097 cfg.background = false;
37099 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37102 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37103 //console.log('adding nested layout panel ' + cfg.toSource());
37104 this.add(region, ret);
37105 nb = {}; /// find first...
37110 // needs grid and region
37112 //var el = this.getRegion(region).el.createChild();
37114 *var el = this.el.createChild();
37115 // create the grid first...
37116 cfg.grid.container = el;
37117 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37120 if (region == 'center' && this.active ) {
37121 cfg.background = false;
37124 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37126 this.add(region, ret);
37128 if (cfg.background) {
37129 // render grid on panel activation (if panel background)
37130 ret.on('activate', function(gp) {
37131 if (!gp.grid.rendered) {
37132 // gp.grid.render(el);
37136 // cfg.grid.render(el);
37142 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37143 // it was the old xcomponent building that caused this before.
37144 // espeically if border is the top element in the tree.
37154 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37156 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37157 this.add(region, ret);
37161 throw "Can not add '" + cfg.xtype + "' to Border";
37167 this.beginUpdate();
37171 Roo.each(xitems, function(i) {
37172 region = nb && i.region ? i.region : false;
37174 var add = ret.addxtype(i);
37177 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37178 if (!i.background) {
37179 abn[region] = nb[region] ;
37186 // make the last non-background panel active..
37187 //if (nb) { Roo.log(abn); }
37190 for(var r in abn) {
37191 region = this.getRegion(r);
37193 // tried using nb[r], but it does not work..
37195 region.showPanel(abn[r]);
37206 factory : function(cfg)
37209 var validRegions = Roo.bootstrap.layout.Border.regions;
37211 var target = cfg.region;
37214 var r = Roo.bootstrap.layout;
37218 return new r.North(cfg);
37220 return new r.South(cfg);
37222 return new r.East(cfg);
37224 return new r.West(cfg);
37226 return new r.Center(cfg);
37228 throw 'Layout region "'+target+'" not supported.';
37235 * Ext JS Library 1.1.1
37236 * Copyright(c) 2006-2007, Ext JS, LLC.
37238 * Originally Released Under LGPL - original licence link has changed is not relivant.
37241 * <script type="text/javascript">
37245 * @class Roo.bootstrap.layout.Basic
37246 * @extends Roo.util.Observable
37247 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37248 * and does not have a titlebar, tabs or any other features. All it does is size and position
37249 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37250 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37251 * @cfg {string} region the region that it inhabits..
37252 * @cfg {bool} skipConfig skip config?
37256 Roo.bootstrap.layout.Basic = function(config){
37258 this.mgr = config.mgr;
37260 this.position = config.region;
37262 var skipConfig = config.skipConfig;
37266 * @scope Roo.BasicLayoutRegion
37270 * @event beforeremove
37271 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37272 * @param {Roo.LayoutRegion} this
37273 * @param {Roo.ContentPanel} panel The panel
37274 * @param {Object} e The cancel event object
37276 "beforeremove" : true,
37278 * @event invalidated
37279 * Fires when the layout for this region is changed.
37280 * @param {Roo.LayoutRegion} this
37282 "invalidated" : true,
37284 * @event visibilitychange
37285 * Fires when this region is shown or hidden
37286 * @param {Roo.LayoutRegion} this
37287 * @param {Boolean} visibility true or false
37289 "visibilitychange" : true,
37291 * @event paneladded
37292 * Fires when a panel is added.
37293 * @param {Roo.LayoutRegion} this
37294 * @param {Roo.ContentPanel} panel The panel
37296 "paneladded" : true,
37298 * @event panelremoved
37299 * Fires when a panel is removed.
37300 * @param {Roo.LayoutRegion} this
37301 * @param {Roo.ContentPanel} panel The panel
37303 "panelremoved" : true,
37305 * @event beforecollapse
37306 * Fires when this region before collapse.
37307 * @param {Roo.LayoutRegion} this
37309 "beforecollapse" : true,
37312 * Fires when this region is collapsed.
37313 * @param {Roo.LayoutRegion} this
37315 "collapsed" : true,
37318 * Fires when this region is expanded.
37319 * @param {Roo.LayoutRegion} this
37324 * Fires when this region is slid into view.
37325 * @param {Roo.LayoutRegion} this
37327 "slideshow" : true,
37330 * Fires when this region slides out of view.
37331 * @param {Roo.LayoutRegion} this
37333 "slidehide" : true,
37335 * @event panelactivated
37336 * Fires when a panel is activated.
37337 * @param {Roo.LayoutRegion} this
37338 * @param {Roo.ContentPanel} panel The activated panel
37340 "panelactivated" : true,
37343 * Fires when the user resizes this region.
37344 * @param {Roo.LayoutRegion} this
37345 * @param {Number} newSize The new size (width for east/west, height for north/south)
37349 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37350 this.panels = new Roo.util.MixedCollection();
37351 this.panels.getKey = this.getPanelId.createDelegate(this);
37353 this.activePanel = null;
37354 // ensure listeners are added...
37356 if (config.listeners || config.events) {
37357 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37358 listeners : config.listeners || {},
37359 events : config.events || {}
37363 if(skipConfig !== true){
37364 this.applyConfig(config);
37368 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37370 getPanelId : function(p){
37374 applyConfig : function(config){
37375 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37376 this.config = config;
37381 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37382 * the width, for horizontal (north, south) the height.
37383 * @param {Number} newSize The new width or height
37385 resizeTo : function(newSize){
37386 var el = this.el ? this.el :
37387 (this.activePanel ? this.activePanel.getEl() : null);
37389 switch(this.position){
37392 el.setWidth(newSize);
37393 this.fireEvent("resized", this, newSize);
37397 el.setHeight(newSize);
37398 this.fireEvent("resized", this, newSize);
37404 getBox : function(){
37405 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37408 getMargins : function(){
37409 return this.margins;
37412 updateBox : function(box){
37414 var el = this.activePanel.getEl();
37415 el.dom.style.left = box.x + "px";
37416 el.dom.style.top = box.y + "px";
37417 this.activePanel.setSize(box.width, box.height);
37421 * Returns the container element for this region.
37422 * @return {Roo.Element}
37424 getEl : function(){
37425 return this.activePanel;
37429 * Returns true if this region is currently visible.
37430 * @return {Boolean}
37432 isVisible : function(){
37433 return this.activePanel ? true : false;
37436 setActivePanel : function(panel){
37437 panel = this.getPanel(panel);
37438 if(this.activePanel && this.activePanel != panel){
37439 this.activePanel.setActiveState(false);
37440 this.activePanel.getEl().setLeftTop(-10000,-10000);
37442 this.activePanel = panel;
37443 panel.setActiveState(true);
37445 panel.setSize(this.box.width, this.box.height);
37447 this.fireEvent("panelactivated", this, panel);
37448 this.fireEvent("invalidated");
37452 * Show the specified panel.
37453 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37454 * @return {Roo.ContentPanel} The shown panel or null
37456 showPanel : function(panel){
37457 panel = this.getPanel(panel);
37459 this.setActivePanel(panel);
37465 * Get the active panel for this region.
37466 * @return {Roo.ContentPanel} The active panel or null
37468 getActivePanel : function(){
37469 return this.activePanel;
37473 * Add the passed ContentPanel(s)
37474 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37475 * @return {Roo.ContentPanel} The panel added (if only one was added)
37477 add : function(panel){
37478 if(arguments.length > 1){
37479 for(var i = 0, len = arguments.length; i < len; i++) {
37480 this.add(arguments[i]);
37484 if(this.hasPanel(panel)){
37485 this.showPanel(panel);
37488 var el = panel.getEl();
37489 if(el.dom.parentNode != this.mgr.el.dom){
37490 this.mgr.el.dom.appendChild(el.dom);
37492 if(panel.setRegion){
37493 panel.setRegion(this);
37495 this.panels.add(panel);
37496 el.setStyle("position", "absolute");
37497 if(!panel.background){
37498 this.setActivePanel(panel);
37499 if(this.config.initialSize && this.panels.getCount()==1){
37500 this.resizeTo(this.config.initialSize);
37503 this.fireEvent("paneladded", this, panel);
37508 * Returns true if the panel is in this region.
37509 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37510 * @return {Boolean}
37512 hasPanel : function(panel){
37513 if(typeof panel == "object"){ // must be panel obj
37514 panel = panel.getId();
37516 return this.getPanel(panel) ? true : false;
37520 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37521 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37522 * @param {Boolean} preservePanel Overrides the config preservePanel option
37523 * @return {Roo.ContentPanel} The panel that was removed
37525 remove : function(panel, preservePanel){
37526 panel = this.getPanel(panel);
37531 this.fireEvent("beforeremove", this, panel, e);
37532 if(e.cancel === true){
37535 var panelId = panel.getId();
37536 this.panels.removeKey(panelId);
37541 * Returns the panel specified or null if it's not in this region.
37542 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37543 * @return {Roo.ContentPanel}
37545 getPanel : function(id){
37546 if(typeof id == "object"){ // must be panel obj
37549 return this.panels.get(id);
37553 * Returns this regions position (north/south/east/west/center).
37556 getPosition: function(){
37557 return this.position;
37561 * Ext JS Library 1.1.1
37562 * Copyright(c) 2006-2007, Ext JS, LLC.
37564 * Originally Released Under LGPL - original licence link has changed is not relivant.
37567 * <script type="text/javascript">
37571 * @class Roo.bootstrap.layout.Region
37572 * @extends Roo.bootstrap.layout.Basic
37573 * This class represents a region in a layout manager.
37575 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37576 * @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})
37577 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37578 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37579 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37580 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37581 * @cfg {String} title The title for the region (overrides panel titles)
37582 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37583 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37584 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37585 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37586 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37587 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37588 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37589 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37590 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37591 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37593 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37594 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37595 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37596 * @cfg {Number} width For East/West panels
37597 * @cfg {Number} height For North/South panels
37598 * @cfg {Boolean} split To show the splitter
37599 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37601 * @cfg {string} cls Extra CSS classes to add to region
37603 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37604 * @cfg {string} region the region that it inhabits..
37607 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37608 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37610 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37611 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37612 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37614 Roo.bootstrap.layout.Region = function(config)
37616 this.applyConfig(config);
37618 var mgr = config.mgr;
37619 var pos = config.region;
37620 config.skipConfig = true;
37621 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37624 this.onRender(mgr.el);
37627 this.visible = true;
37628 this.collapsed = false;
37629 this.unrendered_panels = [];
37632 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37634 position: '', // set by wrapper (eg. north/south etc..)
37635 unrendered_panels : null, // unrendered panels.
37637 tabPosition : false,
37639 mgr: false, // points to 'Border'
37642 createBody : function(){
37643 /** This region's body element
37644 * @type Roo.Element */
37645 this.bodyEl = this.el.createChild({
37647 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37651 onRender: function(ctr, pos)
37653 var dh = Roo.DomHelper;
37654 /** This region's container element
37655 * @type Roo.Element */
37656 this.el = dh.append(ctr.dom, {
37658 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37660 /** This region's title element
37661 * @type Roo.Element */
37663 this.titleEl = dh.append(this.el.dom, {
37665 unselectable: "on",
37666 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37668 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37669 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37673 this.titleEl.enableDisplayMode();
37674 /** This region's title text element
37675 * @type HTMLElement */
37676 this.titleTextEl = this.titleEl.dom.firstChild;
37677 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37679 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37680 this.closeBtn.enableDisplayMode();
37681 this.closeBtn.on("click", this.closeClicked, this);
37682 this.closeBtn.hide();
37684 this.createBody(this.config);
37685 if(this.config.hideWhenEmpty){
37687 this.on("paneladded", this.validateVisibility, this);
37688 this.on("panelremoved", this.validateVisibility, this);
37690 if(this.autoScroll){
37691 this.bodyEl.setStyle("overflow", "auto");
37693 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37695 //if(c.titlebar !== false){
37696 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37697 this.titleEl.hide();
37699 this.titleEl.show();
37700 if(this.config.title){
37701 this.titleTextEl.innerHTML = this.config.title;
37705 if(this.config.collapsed){
37706 this.collapse(true);
37708 if(this.config.hidden){
37712 if (this.unrendered_panels && this.unrendered_panels.length) {
37713 for (var i =0;i< this.unrendered_panels.length; i++) {
37714 this.add(this.unrendered_panels[i]);
37716 this.unrendered_panels = null;
37722 applyConfig : function(c)
37725 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37726 var dh = Roo.DomHelper;
37727 if(c.titlebar !== false){
37728 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37729 this.collapseBtn.on("click", this.collapse, this);
37730 this.collapseBtn.enableDisplayMode();
37732 if(c.showPin === true || this.showPin){
37733 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37734 this.stickBtn.enableDisplayMode();
37735 this.stickBtn.on("click", this.expand, this);
37736 this.stickBtn.hide();
37741 /** This region's collapsed element
37742 * @type Roo.Element */
37745 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37746 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37749 if(c.floatable !== false){
37750 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37751 this.collapsedEl.on("click", this.collapseClick, this);
37754 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37755 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37756 id: "message", unselectable: "on", style:{"float":"left"}});
37757 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37759 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37760 this.expandBtn.on("click", this.expand, this);
37764 if(this.collapseBtn){
37765 this.collapseBtn.setVisible(c.collapsible == true);
37768 this.cmargins = c.cmargins || this.cmargins ||
37769 (this.position == "west" || this.position == "east" ?
37770 {top: 0, left: 2, right:2, bottom: 0} :
37771 {top: 2, left: 0, right:0, bottom: 2});
37773 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37776 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37778 this.autoScroll = c.autoScroll || false;
37783 this.duration = c.duration || .30;
37784 this.slideDuration = c.slideDuration || .45;
37789 * Returns true if this region is currently visible.
37790 * @return {Boolean}
37792 isVisible : function(){
37793 return this.visible;
37797 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37798 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37800 //setCollapsedTitle : function(title){
37801 // title = title || " ";
37802 // if(this.collapsedTitleTextEl){
37803 // this.collapsedTitleTextEl.innerHTML = title;
37807 getBox : function(){
37809 // if(!this.collapsed){
37810 b = this.el.getBox(false, true);
37812 // b = this.collapsedEl.getBox(false, true);
37817 getMargins : function(){
37818 return this.margins;
37819 //return this.collapsed ? this.cmargins : this.margins;
37822 highlight : function(){
37823 this.el.addClass("x-layout-panel-dragover");
37826 unhighlight : function(){
37827 this.el.removeClass("x-layout-panel-dragover");
37830 updateBox : function(box)
37832 if (!this.bodyEl) {
37833 return; // not rendered yet..
37837 if(!this.collapsed){
37838 this.el.dom.style.left = box.x + "px";
37839 this.el.dom.style.top = box.y + "px";
37840 this.updateBody(box.width, box.height);
37842 this.collapsedEl.dom.style.left = box.x + "px";
37843 this.collapsedEl.dom.style.top = box.y + "px";
37844 this.collapsedEl.setSize(box.width, box.height);
37847 this.tabs.autoSizeTabs();
37851 updateBody : function(w, h)
37854 this.el.setWidth(w);
37855 w -= this.el.getBorderWidth("rl");
37856 if(this.config.adjustments){
37857 w += this.config.adjustments[0];
37860 if(h !== null && h > 0){
37861 this.el.setHeight(h);
37862 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37863 h -= this.el.getBorderWidth("tb");
37864 if(this.config.adjustments){
37865 h += this.config.adjustments[1];
37867 this.bodyEl.setHeight(h);
37869 h = this.tabs.syncHeight(h);
37872 if(this.panelSize){
37873 w = w !== null ? w : this.panelSize.width;
37874 h = h !== null ? h : this.panelSize.height;
37876 if(this.activePanel){
37877 var el = this.activePanel.getEl();
37878 w = w !== null ? w : el.getWidth();
37879 h = h !== null ? h : el.getHeight();
37880 this.panelSize = {width: w, height: h};
37881 this.activePanel.setSize(w, h);
37883 if(Roo.isIE && this.tabs){
37884 this.tabs.el.repaint();
37889 * Returns the container element for this region.
37890 * @return {Roo.Element}
37892 getEl : function(){
37897 * Hides this region.
37900 //if(!this.collapsed){
37901 this.el.dom.style.left = "-2000px";
37904 // this.collapsedEl.dom.style.left = "-2000px";
37905 // this.collapsedEl.hide();
37907 this.visible = false;
37908 this.fireEvent("visibilitychange", this, false);
37912 * Shows this region if it was previously hidden.
37915 //if(!this.collapsed){
37918 // this.collapsedEl.show();
37920 this.visible = true;
37921 this.fireEvent("visibilitychange", this, true);
37924 closeClicked : function(){
37925 if(this.activePanel){
37926 this.remove(this.activePanel);
37930 collapseClick : function(e){
37932 e.stopPropagation();
37935 e.stopPropagation();
37941 * Collapses this region.
37942 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37945 collapse : function(skipAnim, skipCheck = false){
37946 if(this.collapsed) {
37950 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37952 this.collapsed = true;
37954 this.split.el.hide();
37956 if(this.config.animate && skipAnim !== true){
37957 this.fireEvent("invalidated", this);
37958 this.animateCollapse();
37960 this.el.setLocation(-20000,-20000);
37962 this.collapsedEl.show();
37963 this.fireEvent("collapsed", this);
37964 this.fireEvent("invalidated", this);
37970 animateCollapse : function(){
37975 * Expands this region if it was previously collapsed.
37976 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37977 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37980 expand : function(e, skipAnim){
37982 e.stopPropagation();
37984 if(!this.collapsed || this.el.hasActiveFx()) {
37988 this.afterSlideIn();
37991 this.collapsed = false;
37992 if(this.config.animate && skipAnim !== true){
37993 this.animateExpand();
37997 this.split.el.show();
37999 this.collapsedEl.setLocation(-2000,-2000);
38000 this.collapsedEl.hide();
38001 this.fireEvent("invalidated", this);
38002 this.fireEvent("expanded", this);
38006 animateExpand : function(){
38010 initTabs : function()
38012 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38014 var ts = new Roo.bootstrap.panel.Tabs({
38015 el: this.bodyEl.dom,
38017 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38018 disableTooltips: this.config.disableTabTips,
38019 toolbar : this.config.toolbar
38022 if(this.config.hideTabs){
38023 ts.stripWrap.setDisplayed(false);
38026 ts.resizeTabs = this.config.resizeTabs === true;
38027 ts.minTabWidth = this.config.minTabWidth || 40;
38028 ts.maxTabWidth = this.config.maxTabWidth || 250;
38029 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38030 ts.monitorResize = false;
38031 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38032 ts.bodyEl.addClass('roo-layout-tabs-body');
38033 this.panels.each(this.initPanelAsTab, this);
38036 initPanelAsTab : function(panel){
38037 var ti = this.tabs.addTab(
38041 this.config.closeOnTab && panel.isClosable(),
38044 if(panel.tabTip !== undefined){
38045 ti.setTooltip(panel.tabTip);
38047 ti.on("activate", function(){
38048 this.setActivePanel(panel);
38051 if(this.config.closeOnTab){
38052 ti.on("beforeclose", function(t, e){
38054 this.remove(panel);
38058 panel.tabItem = ti;
38063 updatePanelTitle : function(panel, title)
38065 if(this.activePanel == panel){
38066 this.updateTitle(title);
38069 var ti = this.tabs.getTab(panel.getEl().id);
38071 if(panel.tabTip !== undefined){
38072 ti.setTooltip(panel.tabTip);
38077 updateTitle : function(title){
38078 if(this.titleTextEl && !this.config.title){
38079 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38083 setActivePanel : function(panel)
38085 panel = this.getPanel(panel);
38086 if(this.activePanel && this.activePanel != panel){
38087 if(this.activePanel.setActiveState(false) === false){
38091 this.activePanel = panel;
38092 panel.setActiveState(true);
38093 if(this.panelSize){
38094 panel.setSize(this.panelSize.width, this.panelSize.height);
38097 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38099 this.updateTitle(panel.getTitle());
38101 this.fireEvent("invalidated", this);
38103 this.fireEvent("panelactivated", this, panel);
38107 * Shows the specified panel.
38108 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38109 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38111 showPanel : function(panel)
38113 panel = this.getPanel(panel);
38116 var tab = this.tabs.getTab(panel.getEl().id);
38117 if(tab.isHidden()){
38118 this.tabs.unhideTab(tab.id);
38122 this.setActivePanel(panel);
38129 * Get the active panel for this region.
38130 * @return {Roo.ContentPanel} The active panel or null
38132 getActivePanel : function(){
38133 return this.activePanel;
38136 validateVisibility : function(){
38137 if(this.panels.getCount() < 1){
38138 this.updateTitle(" ");
38139 this.closeBtn.hide();
38142 if(!this.isVisible()){
38149 * Adds the passed ContentPanel(s) to this region.
38150 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38151 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38153 add : function(panel)
38155 if(arguments.length > 1){
38156 for(var i = 0, len = arguments.length; i < len; i++) {
38157 this.add(arguments[i]);
38162 // if we have not been rendered yet, then we can not really do much of this..
38163 if (!this.bodyEl) {
38164 this.unrendered_panels.push(panel);
38171 if(this.hasPanel(panel)){
38172 this.showPanel(panel);
38175 panel.setRegion(this);
38176 this.panels.add(panel);
38177 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38178 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38179 // and hide them... ???
38180 this.bodyEl.dom.appendChild(panel.getEl().dom);
38181 if(panel.background !== true){
38182 this.setActivePanel(panel);
38184 this.fireEvent("paneladded", this, panel);
38191 this.initPanelAsTab(panel);
38195 if(panel.background !== true){
38196 this.tabs.activate(panel.getEl().id);
38198 this.fireEvent("paneladded", this, panel);
38203 * Hides the tab for the specified panel.
38204 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38206 hidePanel : function(panel){
38207 if(this.tabs && (panel = this.getPanel(panel))){
38208 this.tabs.hideTab(panel.getEl().id);
38213 * Unhides the tab for a previously hidden panel.
38214 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38216 unhidePanel : function(panel){
38217 if(this.tabs && (panel = this.getPanel(panel))){
38218 this.tabs.unhideTab(panel.getEl().id);
38222 clearPanels : function(){
38223 while(this.panels.getCount() > 0){
38224 this.remove(this.panels.first());
38229 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38230 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38231 * @param {Boolean} preservePanel Overrides the config preservePanel option
38232 * @return {Roo.ContentPanel} The panel that was removed
38234 remove : function(panel, preservePanel)
38236 panel = this.getPanel(panel);
38241 this.fireEvent("beforeremove", this, panel, e);
38242 if(e.cancel === true){
38245 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38246 var panelId = panel.getId();
38247 this.panels.removeKey(panelId);
38249 document.body.appendChild(panel.getEl().dom);
38252 this.tabs.removeTab(panel.getEl().id);
38253 }else if (!preservePanel){
38254 this.bodyEl.dom.removeChild(panel.getEl().dom);
38256 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38257 var p = this.panels.first();
38258 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38259 tempEl.appendChild(p.getEl().dom);
38260 this.bodyEl.update("");
38261 this.bodyEl.dom.appendChild(p.getEl().dom);
38263 this.updateTitle(p.getTitle());
38265 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38266 this.setActivePanel(p);
38268 panel.setRegion(null);
38269 if(this.activePanel == panel){
38270 this.activePanel = null;
38272 if(this.config.autoDestroy !== false && preservePanel !== true){
38273 try{panel.destroy();}catch(e){}
38275 this.fireEvent("panelremoved", this, panel);
38280 * Returns the TabPanel component used by this region
38281 * @return {Roo.TabPanel}
38283 getTabs : function(){
38287 createTool : function(parentEl, className){
38288 var btn = Roo.DomHelper.append(parentEl, {
38290 cls: "x-layout-tools-button",
38293 cls: "roo-layout-tools-button-inner " + className,
38297 btn.addClassOnOver("roo-layout-tools-button-over");
38302 * Ext JS Library 1.1.1
38303 * Copyright(c) 2006-2007, Ext JS, LLC.
38305 * Originally Released Under LGPL - original licence link has changed is not relivant.
38308 * <script type="text/javascript">
38314 * @class Roo.SplitLayoutRegion
38315 * @extends Roo.LayoutRegion
38316 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38318 Roo.bootstrap.layout.Split = function(config){
38319 this.cursor = config.cursor;
38320 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38323 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38325 splitTip : "Drag to resize.",
38326 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38327 useSplitTips : false,
38329 applyConfig : function(config){
38330 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38333 onRender : function(ctr,pos) {
38335 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38336 if(!this.config.split){
38341 var splitEl = Roo.DomHelper.append(ctr.dom, {
38343 id: this.el.id + "-split",
38344 cls: "roo-layout-split roo-layout-split-"+this.position,
38347 /** The SplitBar for this region
38348 * @type Roo.SplitBar */
38349 // does not exist yet...
38350 Roo.log([this.position, this.orientation]);
38352 this.split = new Roo.bootstrap.SplitBar({
38353 dragElement : splitEl,
38354 resizingElement: this.el,
38355 orientation : this.orientation
38358 this.split.on("moved", this.onSplitMove, this);
38359 this.split.useShim = this.config.useShim === true;
38360 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38361 if(this.useSplitTips){
38362 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38364 //if(config.collapsible){
38365 // this.split.el.on("dblclick", this.collapse, this);
38368 if(typeof this.config.minSize != "undefined"){
38369 this.split.minSize = this.config.minSize;
38371 if(typeof this.config.maxSize != "undefined"){
38372 this.split.maxSize = this.config.maxSize;
38374 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38375 this.hideSplitter();
38380 getHMaxSize : function(){
38381 var cmax = this.config.maxSize || 10000;
38382 var center = this.mgr.getRegion("center");
38383 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38386 getVMaxSize : function(){
38387 var cmax = this.config.maxSize || 10000;
38388 var center = this.mgr.getRegion("center");
38389 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38392 onSplitMove : function(split, newSize){
38393 this.fireEvent("resized", this, newSize);
38397 * Returns the {@link Roo.SplitBar} for this region.
38398 * @return {Roo.SplitBar}
38400 getSplitBar : function(){
38405 this.hideSplitter();
38406 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38409 hideSplitter : function(){
38411 this.split.el.setLocation(-2000,-2000);
38412 this.split.el.hide();
38418 this.split.el.show();
38420 Roo.bootstrap.layout.Split.superclass.show.call(this);
38423 beforeSlide: function(){
38424 if(Roo.isGecko){// firefox overflow auto bug workaround
38425 this.bodyEl.clip();
38427 this.tabs.bodyEl.clip();
38429 if(this.activePanel){
38430 this.activePanel.getEl().clip();
38432 if(this.activePanel.beforeSlide){
38433 this.activePanel.beforeSlide();
38439 afterSlide : function(){
38440 if(Roo.isGecko){// firefox overflow auto bug workaround
38441 this.bodyEl.unclip();
38443 this.tabs.bodyEl.unclip();
38445 if(this.activePanel){
38446 this.activePanel.getEl().unclip();
38447 if(this.activePanel.afterSlide){
38448 this.activePanel.afterSlide();
38454 initAutoHide : function(){
38455 if(this.autoHide !== false){
38456 if(!this.autoHideHd){
38457 var st = new Roo.util.DelayedTask(this.slideIn, this);
38458 this.autoHideHd = {
38459 "mouseout": function(e){
38460 if(!e.within(this.el, true)){
38464 "mouseover" : function(e){
38470 this.el.on(this.autoHideHd);
38474 clearAutoHide : function(){
38475 if(this.autoHide !== false){
38476 this.el.un("mouseout", this.autoHideHd.mouseout);
38477 this.el.un("mouseover", this.autoHideHd.mouseover);
38481 clearMonitor : function(){
38482 Roo.get(document).un("click", this.slideInIf, this);
38485 // these names are backwards but not changed for compat
38486 slideOut : function(){
38487 if(this.isSlid || this.el.hasActiveFx()){
38490 this.isSlid = true;
38491 if(this.collapseBtn){
38492 this.collapseBtn.hide();
38494 this.closeBtnState = this.closeBtn.getStyle('display');
38495 this.closeBtn.hide();
38497 this.stickBtn.show();
38500 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38501 this.beforeSlide();
38502 this.el.setStyle("z-index", 10001);
38503 this.el.slideIn(this.getSlideAnchor(), {
38504 callback: function(){
38506 this.initAutoHide();
38507 Roo.get(document).on("click", this.slideInIf, this);
38508 this.fireEvent("slideshow", this);
38515 afterSlideIn : function(){
38516 this.clearAutoHide();
38517 this.isSlid = false;
38518 this.clearMonitor();
38519 this.el.setStyle("z-index", "");
38520 if(this.collapseBtn){
38521 this.collapseBtn.show();
38523 this.closeBtn.setStyle('display', this.closeBtnState);
38525 this.stickBtn.hide();
38527 this.fireEvent("slidehide", this);
38530 slideIn : function(cb){
38531 if(!this.isSlid || this.el.hasActiveFx()){
38535 this.isSlid = false;
38536 this.beforeSlide();
38537 this.el.slideOut(this.getSlideAnchor(), {
38538 callback: function(){
38539 this.el.setLeftTop(-10000, -10000);
38541 this.afterSlideIn();
38549 slideInIf : function(e){
38550 if(!e.within(this.el)){
38555 animateCollapse : function(){
38556 this.beforeSlide();
38557 this.el.setStyle("z-index", 20000);
38558 var anchor = this.getSlideAnchor();
38559 this.el.slideOut(anchor, {
38560 callback : function(){
38561 this.el.setStyle("z-index", "");
38562 this.collapsedEl.slideIn(anchor, {duration:.3});
38564 this.el.setLocation(-10000,-10000);
38566 this.fireEvent("collapsed", this);
38573 animateExpand : function(){
38574 this.beforeSlide();
38575 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38576 this.el.setStyle("z-index", 20000);
38577 this.collapsedEl.hide({
38580 this.el.slideIn(this.getSlideAnchor(), {
38581 callback : function(){
38582 this.el.setStyle("z-index", "");
38585 this.split.el.show();
38587 this.fireEvent("invalidated", this);
38588 this.fireEvent("expanded", this);
38616 getAnchor : function(){
38617 return this.anchors[this.position];
38620 getCollapseAnchor : function(){
38621 return this.canchors[this.position];
38624 getSlideAnchor : function(){
38625 return this.sanchors[this.position];
38628 getAlignAdj : function(){
38629 var cm = this.cmargins;
38630 switch(this.position){
38646 getExpandAdj : function(){
38647 var c = this.collapsedEl, cm = this.cmargins;
38648 switch(this.position){
38650 return [-(cm.right+c.getWidth()+cm.left), 0];
38653 return [cm.right+c.getWidth()+cm.left, 0];
38656 return [0, -(cm.top+cm.bottom+c.getHeight())];
38659 return [0, cm.top+cm.bottom+c.getHeight()];
38665 * Ext JS Library 1.1.1
38666 * Copyright(c) 2006-2007, Ext JS, LLC.
38668 * Originally Released Under LGPL - original licence link has changed is not relivant.
38671 * <script type="text/javascript">
38674 * These classes are private internal classes
38676 Roo.bootstrap.layout.Center = function(config){
38677 config.region = "center";
38678 Roo.bootstrap.layout.Region.call(this, config);
38679 this.visible = true;
38680 this.minWidth = config.minWidth || 20;
38681 this.minHeight = config.minHeight || 20;
38684 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38686 // center panel can't be hidden
38690 // center panel can't be hidden
38693 getMinWidth: function(){
38694 return this.minWidth;
38697 getMinHeight: function(){
38698 return this.minHeight;
38712 Roo.bootstrap.layout.North = function(config)
38714 config.region = 'north';
38715 config.cursor = 'n-resize';
38717 Roo.bootstrap.layout.Split.call(this, config);
38721 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38722 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38723 this.split.el.addClass("roo-layout-split-v");
38725 var size = config.initialSize || config.height;
38726 if(typeof size != "undefined"){
38727 this.el.setHeight(size);
38730 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38732 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38736 getBox : function(){
38737 if(this.collapsed){
38738 return this.collapsedEl.getBox();
38740 var box = this.el.getBox();
38742 box.height += this.split.el.getHeight();
38747 updateBox : function(box){
38748 if(this.split && !this.collapsed){
38749 box.height -= this.split.el.getHeight();
38750 this.split.el.setLeft(box.x);
38751 this.split.el.setTop(box.y+box.height);
38752 this.split.el.setWidth(box.width);
38754 if(this.collapsed){
38755 this.updateBody(box.width, null);
38757 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38765 Roo.bootstrap.layout.South = function(config){
38766 config.region = 'south';
38767 config.cursor = 's-resize';
38768 Roo.bootstrap.layout.Split.call(this, config);
38770 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38771 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38772 this.split.el.addClass("roo-layout-split-v");
38774 var size = config.initialSize || config.height;
38775 if(typeof size != "undefined"){
38776 this.el.setHeight(size);
38780 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38781 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38782 getBox : function(){
38783 if(this.collapsed){
38784 return this.collapsedEl.getBox();
38786 var box = this.el.getBox();
38788 var sh = this.split.el.getHeight();
38795 updateBox : function(box){
38796 if(this.split && !this.collapsed){
38797 var sh = this.split.el.getHeight();
38800 this.split.el.setLeft(box.x);
38801 this.split.el.setTop(box.y-sh);
38802 this.split.el.setWidth(box.width);
38804 if(this.collapsed){
38805 this.updateBody(box.width, null);
38807 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38811 Roo.bootstrap.layout.East = function(config){
38812 config.region = "east";
38813 config.cursor = "e-resize";
38814 Roo.bootstrap.layout.Split.call(this, config);
38816 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38817 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38818 this.split.el.addClass("roo-layout-split-h");
38820 var size = config.initialSize || config.width;
38821 if(typeof size != "undefined"){
38822 this.el.setWidth(size);
38825 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38826 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38827 getBox : function(){
38828 if(this.collapsed){
38829 return this.collapsedEl.getBox();
38831 var box = this.el.getBox();
38833 var sw = this.split.el.getWidth();
38840 updateBox : function(box){
38841 if(this.split && !this.collapsed){
38842 var sw = this.split.el.getWidth();
38844 this.split.el.setLeft(box.x);
38845 this.split.el.setTop(box.y);
38846 this.split.el.setHeight(box.height);
38849 if(this.collapsed){
38850 this.updateBody(null, box.height);
38852 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38856 Roo.bootstrap.layout.West = function(config){
38857 config.region = "west";
38858 config.cursor = "w-resize";
38860 Roo.bootstrap.layout.Split.call(this, config);
38862 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38863 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38864 this.split.el.addClass("roo-layout-split-h");
38868 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38869 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38871 onRender: function(ctr, pos)
38873 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38874 var size = this.config.initialSize || this.config.width;
38875 if(typeof size != "undefined"){
38876 this.el.setWidth(size);
38880 getBox : function(){
38881 if(this.collapsed){
38882 return this.collapsedEl.getBox();
38884 var box = this.el.getBox();
38886 box.width += this.split.el.getWidth();
38891 updateBox : function(box){
38892 if(this.split && !this.collapsed){
38893 var sw = this.split.el.getWidth();
38895 this.split.el.setLeft(box.x+box.width);
38896 this.split.el.setTop(box.y);
38897 this.split.el.setHeight(box.height);
38899 if(this.collapsed){
38900 this.updateBody(null, box.height);
38902 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38904 });Roo.namespace("Roo.bootstrap.panel");/*
38906 * Ext JS Library 1.1.1
38907 * Copyright(c) 2006-2007, Ext JS, LLC.
38909 * Originally Released Under LGPL - original licence link has changed is not relivant.
38912 * <script type="text/javascript">
38915 * @class Roo.ContentPanel
38916 * @extends Roo.util.Observable
38917 * A basic ContentPanel element.
38918 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38919 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38920 * @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
38921 * @cfg {Boolean} closable True if the panel can be closed/removed
38922 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38923 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38924 * @cfg {Toolbar} toolbar A toolbar for this panel
38925 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38926 * @cfg {String} title The title for this panel
38927 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38928 * @cfg {String} url Calls {@link #setUrl} with this value
38929 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38930 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38931 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38932 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38933 * @cfg {Boolean} badges render the badges
38936 * Create a new ContentPanel.
38937 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38938 * @param {String/Object} config A string to set only the title or a config object
38939 * @param {String} content (optional) Set the HTML content for this panel
38940 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38942 Roo.bootstrap.panel.Content = function( config){
38944 this.tpl = config.tpl || false;
38946 var el = config.el;
38947 var content = config.content;
38949 if(config.autoCreate){ // xtype is available if this is called from factory
38952 this.el = Roo.get(el);
38953 if(!this.el && config && config.autoCreate){
38954 if(typeof config.autoCreate == "object"){
38955 if(!config.autoCreate.id){
38956 config.autoCreate.id = config.id||el;
38958 this.el = Roo.DomHelper.append(document.body,
38959 config.autoCreate, true);
38961 var elcfg = { tag: "div",
38962 cls: "roo-layout-inactive-content",
38966 elcfg.html = config.html;
38970 this.el = Roo.DomHelper.append(document.body, elcfg , true);
38973 this.closable = false;
38974 this.loaded = false;
38975 this.active = false;
38978 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38980 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38982 this.wrapEl = this.el; //this.el.wrap();
38984 if (config.toolbar.items) {
38985 ti = config.toolbar.items ;
38986 delete config.toolbar.items ;
38990 this.toolbar.render(this.wrapEl, 'before');
38991 for(var i =0;i < ti.length;i++) {
38992 // Roo.log(['add child', items[i]]);
38993 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38995 this.toolbar.items = nitems;
38996 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38997 delete config.toolbar;
39001 // xtype created footer. - not sure if will work as we normally have to render first..
39002 if (this.footer && !this.footer.el && this.footer.xtype) {
39003 if (!this.wrapEl) {
39004 this.wrapEl = this.el.wrap();
39007 this.footer.container = this.wrapEl.createChild();
39009 this.footer = Roo.factory(this.footer, Roo);
39014 if(typeof config == "string"){
39015 this.title = config;
39017 Roo.apply(this, config);
39021 this.resizeEl = Roo.get(this.resizeEl, true);
39023 this.resizeEl = this.el;
39025 // handle view.xtype
39033 * Fires when this panel is activated.
39034 * @param {Roo.ContentPanel} this
39038 * @event deactivate
39039 * Fires when this panel is activated.
39040 * @param {Roo.ContentPanel} this
39042 "deactivate" : true,
39046 * Fires when this panel is resized if fitToFrame is true.
39047 * @param {Roo.ContentPanel} this
39048 * @param {Number} width The width after any component adjustments
39049 * @param {Number} height The height after any component adjustments
39055 * Fires when this tab is created
39056 * @param {Roo.ContentPanel} this
39067 if(this.autoScroll){
39068 this.resizeEl.setStyle("overflow", "auto");
39070 // fix randome scrolling
39071 //this.el.on('scroll', function() {
39072 // Roo.log('fix random scolling');
39073 // this.scrollTo('top',0);
39076 content = content || this.content;
39078 this.setContent(content);
39080 if(config && config.url){
39081 this.setUrl(this.url, this.params, this.loadOnce);
39086 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39088 if (this.view && typeof(this.view.xtype) != 'undefined') {
39089 this.view.el = this.el.appendChild(document.createElement("div"));
39090 this.view = Roo.factory(this.view);
39091 this.view.render && this.view.render(false, '');
39095 this.fireEvent('render', this);
39098 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39102 setRegion : function(region){
39103 this.region = region;
39104 this.setActiveClass(region && !this.background);
39108 setActiveClass: function(state)
39111 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39112 this.el.setStyle('position','relative');
39114 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39115 this.el.setStyle('position', 'absolute');
39120 * Returns the toolbar for this Panel if one was configured.
39121 * @return {Roo.Toolbar}
39123 getToolbar : function(){
39124 return this.toolbar;
39127 setActiveState : function(active)
39129 this.active = active;
39130 this.setActiveClass(active);
39132 if(this.fireEvent("deactivate", this) === false){
39137 this.fireEvent("activate", this);
39141 * Updates this panel's element
39142 * @param {String} content The new content
39143 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39145 setContent : function(content, loadScripts){
39146 this.el.update(content, loadScripts);
39149 ignoreResize : function(w, h){
39150 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39153 this.lastSize = {width: w, height: h};
39158 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39159 * @return {Roo.UpdateManager} The UpdateManager
39161 getUpdateManager : function(){
39162 return this.el.getUpdateManager();
39165 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39166 * @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:
39169 url: "your-url.php",
39170 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39171 callback: yourFunction,
39172 scope: yourObject, //(optional scope)
39175 text: "Loading...",
39180 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39181 * 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.
39182 * @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}
39183 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39184 * @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.
39185 * @return {Roo.ContentPanel} this
39188 var um = this.el.getUpdateManager();
39189 um.update.apply(um, arguments);
39195 * 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.
39196 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39197 * @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)
39198 * @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)
39199 * @return {Roo.UpdateManager} The UpdateManager
39201 setUrl : function(url, params, loadOnce){
39202 if(this.refreshDelegate){
39203 this.removeListener("activate", this.refreshDelegate);
39205 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39206 this.on("activate", this.refreshDelegate);
39207 return this.el.getUpdateManager();
39210 _handleRefresh : function(url, params, loadOnce){
39211 if(!loadOnce || !this.loaded){
39212 var updater = this.el.getUpdateManager();
39213 updater.update(url, params, this._setLoaded.createDelegate(this));
39217 _setLoaded : function(){
39218 this.loaded = true;
39222 * Returns this panel's id
39225 getId : function(){
39230 * Returns this panel's element - used by regiosn to add.
39231 * @return {Roo.Element}
39233 getEl : function(){
39234 return this.wrapEl || this.el;
39239 adjustForComponents : function(width, height)
39241 //Roo.log('adjustForComponents ');
39242 if(this.resizeEl != this.el){
39243 width -= this.el.getFrameWidth('lr');
39244 height -= this.el.getFrameWidth('tb');
39247 var te = this.toolbar.getEl();
39248 te.setWidth(width);
39249 height -= te.getHeight();
39252 var te = this.footer.getEl();
39253 te.setWidth(width);
39254 height -= te.getHeight();
39258 if(this.adjustments){
39259 width += this.adjustments[0];
39260 height += this.adjustments[1];
39262 return {"width": width, "height": height};
39265 setSize : function(width, height){
39266 if(this.fitToFrame && !this.ignoreResize(width, height)){
39267 if(this.fitContainer && this.resizeEl != this.el){
39268 this.el.setSize(width, height);
39270 var size = this.adjustForComponents(width, height);
39271 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39272 this.fireEvent('resize', this, size.width, size.height);
39277 * Returns this panel's title
39280 getTitle : function(){
39282 if (typeof(this.title) != 'object') {
39287 for (var k in this.title) {
39288 if (!this.title.hasOwnProperty(k)) {
39292 if (k.indexOf('-') >= 0) {
39293 var s = k.split('-');
39294 for (var i = 0; i<s.length; i++) {
39295 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39298 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39305 * Set this panel's title
39306 * @param {String} title
39308 setTitle : function(title){
39309 this.title = title;
39311 this.region.updatePanelTitle(this, title);
39316 * Returns true is this panel was configured to be closable
39317 * @return {Boolean}
39319 isClosable : function(){
39320 return this.closable;
39323 beforeSlide : function(){
39325 this.resizeEl.clip();
39328 afterSlide : function(){
39330 this.resizeEl.unclip();
39334 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39335 * Will fail silently if the {@link #setUrl} method has not been called.
39336 * This does not activate the panel, just updates its content.
39338 refresh : function(){
39339 if(this.refreshDelegate){
39340 this.loaded = false;
39341 this.refreshDelegate();
39346 * Destroys this panel
39348 destroy : function(){
39349 this.el.removeAllListeners();
39350 var tempEl = document.createElement("span");
39351 tempEl.appendChild(this.el.dom);
39352 tempEl.innerHTML = "";
39358 * form - if the content panel contains a form - this is a reference to it.
39359 * @type {Roo.form.Form}
39363 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39364 * This contains a reference to it.
39370 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39380 * @param {Object} cfg Xtype definition of item to add.
39384 getChildContainer: function () {
39385 return this.getEl();
39390 var ret = new Roo.factory(cfg);
39395 if (cfg.xtype.match(/^Form$/)) {
39398 //if (this.footer) {
39399 // el = this.footer.container.insertSibling(false, 'before');
39401 el = this.el.createChild();
39404 this.form = new Roo.form.Form(cfg);
39407 if ( this.form.allItems.length) {
39408 this.form.render(el.dom);
39412 // should only have one of theses..
39413 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39414 // views.. should not be just added - used named prop 'view''
39416 cfg.el = this.el.appendChild(document.createElement("div"));
39419 var ret = new Roo.factory(cfg);
39421 ret.render && ret.render(false, ''); // render blank..
39431 * @class Roo.bootstrap.panel.Grid
39432 * @extends Roo.bootstrap.panel.Content
39434 * Create a new GridPanel.
39435 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39436 * @param {Object} config A the config object
39442 Roo.bootstrap.panel.Grid = function(config)
39446 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39447 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39449 config.el = this.wrapper;
39450 //this.el = this.wrapper;
39452 if (config.container) {
39453 // ctor'ed from a Border/panel.grid
39456 this.wrapper.setStyle("overflow", "hidden");
39457 this.wrapper.addClass('roo-grid-container');
39462 if(config.toolbar){
39463 var tool_el = this.wrapper.createChild();
39464 this.toolbar = Roo.factory(config.toolbar);
39466 if (config.toolbar.items) {
39467 ti = config.toolbar.items ;
39468 delete config.toolbar.items ;
39472 this.toolbar.render(tool_el);
39473 for(var i =0;i < ti.length;i++) {
39474 // Roo.log(['add child', items[i]]);
39475 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39477 this.toolbar.items = nitems;
39479 delete config.toolbar;
39482 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39483 config.grid.scrollBody = true;;
39484 config.grid.monitorWindowResize = false; // turn off autosizing
39485 config.grid.autoHeight = false;
39486 config.grid.autoWidth = false;
39488 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39490 if (config.background) {
39491 // render grid on panel activation (if panel background)
39492 this.on('activate', function(gp) {
39493 if (!gp.grid.rendered) {
39494 gp.grid.render(this.wrapper);
39495 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39500 this.grid.render(this.wrapper);
39501 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39504 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39505 // ??? needed ??? config.el = this.wrapper;
39510 // xtype created footer. - not sure if will work as we normally have to render first..
39511 if (this.footer && !this.footer.el && this.footer.xtype) {
39513 var ctr = this.grid.getView().getFooterPanel(true);
39514 this.footer.dataSource = this.grid.dataSource;
39515 this.footer = Roo.factory(this.footer, Roo);
39516 this.footer.render(ctr);
39526 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39527 getId : function(){
39528 return this.grid.id;
39532 * Returns the grid for this panel
39533 * @return {Roo.bootstrap.Table}
39535 getGrid : function(){
39539 setSize : function(width, height){
39540 if(!this.ignoreResize(width, height)){
39541 var grid = this.grid;
39542 var size = this.adjustForComponents(width, height);
39543 var gridel = grid.getGridEl();
39544 gridel.setSize(size.width, size.height);
39546 var thd = grid.getGridEl().select('thead',true).first();
39547 var tbd = grid.getGridEl().select('tbody', true).first();
39549 tbd.setSize(width, height - thd.getHeight());
39558 beforeSlide : function(){
39559 this.grid.getView().scroller.clip();
39562 afterSlide : function(){
39563 this.grid.getView().scroller.unclip();
39566 destroy : function(){
39567 this.grid.destroy();
39569 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39574 * @class Roo.bootstrap.panel.Nest
39575 * @extends Roo.bootstrap.panel.Content
39577 * Create a new Panel, that can contain a layout.Border.
39580 * @param {Roo.BorderLayout} layout The layout for this panel
39581 * @param {String/Object} config A string to set only the title or a config object
39583 Roo.bootstrap.panel.Nest = function(config)
39585 // construct with only one argument..
39586 /* FIXME - implement nicer consturctors
39587 if (layout.layout) {
39589 layout = config.layout;
39590 delete config.layout;
39592 if (layout.xtype && !layout.getEl) {
39593 // then layout needs constructing..
39594 layout = Roo.factory(layout, Roo);
39598 config.el = config.layout.getEl();
39600 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39602 config.layout.monitorWindowResize = false; // turn off autosizing
39603 this.layout = config.layout;
39604 this.layout.getEl().addClass("roo-layout-nested-layout");
39605 this.layout.parent = this;
39612 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39614 setSize : function(width, height){
39615 if(!this.ignoreResize(width, height)){
39616 var size = this.adjustForComponents(width, height);
39617 var el = this.layout.getEl();
39618 if (size.height < 1) {
39619 el.setWidth(size.width);
39621 el.setSize(size.width, size.height);
39623 var touch = el.dom.offsetWidth;
39624 this.layout.layout();
39625 // ie requires a double layout on the first pass
39626 if(Roo.isIE && !this.initialized){
39627 this.initialized = true;
39628 this.layout.layout();
39633 // activate all subpanels if not currently active..
39635 setActiveState : function(active){
39636 this.active = active;
39637 this.setActiveClass(active);
39640 this.fireEvent("deactivate", this);
39644 this.fireEvent("activate", this);
39645 // not sure if this should happen before or after..
39646 if (!this.layout) {
39647 return; // should not happen..
39650 for (var r in this.layout.regions) {
39651 reg = this.layout.getRegion(r);
39652 if (reg.getActivePanel()) {
39653 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39654 reg.setActivePanel(reg.getActivePanel());
39657 if (!reg.panels.length) {
39660 reg.showPanel(reg.getPanel(0));
39669 * Returns the nested BorderLayout for this panel
39670 * @return {Roo.BorderLayout}
39672 getLayout : function(){
39673 return this.layout;
39677 * Adds a xtype elements to the layout of the nested panel
39681 xtype : 'ContentPanel',
39688 xtype : 'NestedLayoutPanel',
39694 items : [ ... list of content panels or nested layout panels.. ]
39698 * @param {Object} cfg Xtype definition of item to add.
39700 addxtype : function(cfg) {
39701 return this.layout.addxtype(cfg);
39706 * Ext JS Library 1.1.1
39707 * Copyright(c) 2006-2007, Ext JS, LLC.
39709 * Originally Released Under LGPL - original licence link has changed is not relivant.
39712 * <script type="text/javascript">
39715 * @class Roo.TabPanel
39716 * @extends Roo.util.Observable
39717 * A lightweight tab container.
39721 // basic tabs 1, built from existing content
39722 var tabs = new Roo.TabPanel("tabs1");
39723 tabs.addTab("script", "View Script");
39724 tabs.addTab("markup", "View Markup");
39725 tabs.activate("script");
39727 // more advanced tabs, built from javascript
39728 var jtabs = new Roo.TabPanel("jtabs");
39729 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39731 // set up the UpdateManager
39732 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39733 var updater = tab2.getUpdateManager();
39734 updater.setDefaultUrl("ajax1.htm");
39735 tab2.on('activate', updater.refresh, updater, true);
39737 // Use setUrl for Ajax loading
39738 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39739 tab3.setUrl("ajax2.htm", null, true);
39742 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39745 jtabs.activate("jtabs-1");
39748 * Create a new TabPanel.
39749 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39750 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39752 Roo.bootstrap.panel.Tabs = function(config){
39754 * The container element for this TabPanel.
39755 * @type Roo.Element
39757 this.el = Roo.get(config.el);
39760 if(typeof config == "boolean"){
39761 this.tabPosition = config ? "bottom" : "top";
39763 Roo.apply(this, config);
39767 if(this.tabPosition == "bottom"){
39768 // if tabs are at the bottom = create the body first.
39769 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39770 this.el.addClass("roo-tabs-bottom");
39772 // next create the tabs holders
39774 if (this.tabPosition == "west"){
39776 var reg = this.region; // fake it..
39778 if (!reg.mgr.parent) {
39781 reg = reg.mgr.parent.region;
39783 Roo.log("got nest?");
39785 if (reg.mgr.getRegion('west')) {
39786 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39787 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39788 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39789 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39790 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39798 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39799 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39800 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39801 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39806 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39809 // finally - if tabs are at the top, then create the body last..
39810 if(this.tabPosition != "bottom"){
39811 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39812 * @type Roo.Element
39814 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39815 this.el.addClass("roo-tabs-top");
39819 this.bodyEl.setStyle("position", "relative");
39821 this.active = null;
39822 this.activateDelegate = this.activate.createDelegate(this);
39827 * Fires when the active tab changes
39828 * @param {Roo.TabPanel} this
39829 * @param {Roo.TabPanelItem} activePanel The new active tab
39833 * @event beforetabchange
39834 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39835 * @param {Roo.TabPanel} this
39836 * @param {Object} e Set cancel to true on this object to cancel the tab change
39837 * @param {Roo.TabPanelItem} tab The tab being changed to
39839 "beforetabchange" : true
39842 Roo.EventManager.onWindowResize(this.onResize, this);
39843 this.cpad = this.el.getPadding("lr");
39844 this.hiddenCount = 0;
39847 // toolbar on the tabbar support...
39848 if (this.toolbar) {
39849 alert("no toolbar support yet");
39850 this.toolbar = false;
39852 var tcfg = this.toolbar;
39853 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39854 this.toolbar = new Roo.Toolbar(tcfg);
39855 if (Roo.isSafari) {
39856 var tbl = tcfg.container.child('table', true);
39857 tbl.setAttribute('width', '100%');
39865 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39868 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39870 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39872 tabPosition : "top",
39874 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39876 currentTabWidth : 0,
39878 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39882 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39886 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39888 preferredTabWidth : 175,
39890 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39892 resizeTabs : false,
39894 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39896 monitorResize : true,
39898 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39900 toolbar : false, // set by caller..
39902 region : false, /// set by caller
39904 disableTooltips : true, // not used yet...
39907 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39908 * @param {String} id The id of the div to use <b>or create</b>
39909 * @param {String} text The text for the tab
39910 * @param {String} content (optional) Content to put in the TabPanelItem body
39911 * @param {Boolean} closable (optional) True to create a close icon on the tab
39912 * @return {Roo.TabPanelItem} The created TabPanelItem
39914 addTab : function(id, text, content, closable, tpl)
39916 var item = new Roo.bootstrap.panel.TabItem({
39920 closable : closable,
39923 this.addTabItem(item);
39925 item.setContent(content);
39931 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39932 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39933 * @return {Roo.TabPanelItem}
39935 getTab : function(id){
39936 return this.items[id];
39940 * Hides the {@link Roo.TabPanelItem} with the specified id/index
39941 * @param {String/Number} id The id or index of the TabPanelItem to hide.
39943 hideTab : function(id){
39944 var t = this.items[id];
39947 this.hiddenCount++;
39948 this.autoSizeTabs();
39953 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39954 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39956 unhideTab : function(id){
39957 var t = this.items[id];
39959 t.setHidden(false);
39960 this.hiddenCount--;
39961 this.autoSizeTabs();
39966 * Adds an existing {@link Roo.TabPanelItem}.
39967 * @param {Roo.TabPanelItem} item The TabPanelItem to add
39969 addTabItem : function(item)
39971 this.items[item.id] = item;
39972 this.items.push(item);
39973 this.autoSizeTabs();
39974 // if(this.resizeTabs){
39975 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39976 // this.autoSizeTabs();
39978 // item.autoSize();
39983 * Removes a {@link Roo.TabPanelItem}.
39984 * @param {String/Number} id The id or index of the TabPanelItem to remove.
39986 removeTab : function(id){
39987 var items = this.items;
39988 var tab = items[id];
39989 if(!tab) { return; }
39990 var index = items.indexOf(tab);
39991 if(this.active == tab && items.length > 1){
39992 var newTab = this.getNextAvailable(index);
39997 this.stripEl.dom.removeChild(tab.pnode.dom);
39998 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39999 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40001 items.splice(index, 1);
40002 delete this.items[tab.id];
40003 tab.fireEvent("close", tab);
40004 tab.purgeListeners();
40005 this.autoSizeTabs();
40008 getNextAvailable : function(start){
40009 var items = this.items;
40011 // look for a next tab that will slide over to
40012 // replace the one being removed
40013 while(index < items.length){
40014 var item = items[++index];
40015 if(item && !item.isHidden()){
40019 // if one isn't found select the previous tab (on the left)
40022 var item = items[--index];
40023 if(item && !item.isHidden()){
40031 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40032 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40034 disableTab : function(id){
40035 var tab = this.items[id];
40036 if(tab && this.active != tab){
40042 * Enables a {@link Roo.TabPanelItem} that is disabled.
40043 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40045 enableTab : function(id){
40046 var tab = this.items[id];
40051 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40052 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40053 * @return {Roo.TabPanelItem} The TabPanelItem.
40055 activate : function(id)
40057 //Roo.log('activite:' + id);
40059 var tab = this.items[id];
40063 if(tab == this.active || tab.disabled){
40067 this.fireEvent("beforetabchange", this, e, tab);
40068 if(e.cancel !== true && !tab.disabled){
40070 this.active.hide();
40072 this.active = this.items[id];
40073 this.active.show();
40074 this.fireEvent("tabchange", this, this.active);
40080 * Gets the active {@link Roo.TabPanelItem}.
40081 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40083 getActiveTab : function(){
40084 return this.active;
40088 * Updates the tab body element to fit the height of the container element
40089 * for overflow scrolling
40090 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40092 syncHeight : function(targetHeight){
40093 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40094 var bm = this.bodyEl.getMargins();
40095 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40096 this.bodyEl.setHeight(newHeight);
40100 onResize : function(){
40101 if(this.monitorResize){
40102 this.autoSizeTabs();
40107 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40109 beginUpdate : function(){
40110 this.updating = true;
40114 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40116 endUpdate : function(){
40117 this.updating = false;
40118 this.autoSizeTabs();
40122 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40124 autoSizeTabs : function()
40126 var count = this.items.length;
40127 var vcount = count - this.hiddenCount;
40130 this.stripEl.hide();
40132 this.stripEl.show();
40135 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40140 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40141 var availWidth = Math.floor(w / vcount);
40142 var b = this.stripBody;
40143 if(b.getWidth() > w){
40144 var tabs = this.items;
40145 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40146 if(availWidth < this.minTabWidth){
40147 /*if(!this.sleft){ // incomplete scrolling code
40148 this.createScrollButtons();
40151 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40154 if(this.currentTabWidth < this.preferredTabWidth){
40155 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40161 * Returns the number of tabs in this TabPanel.
40164 getCount : function(){
40165 return this.items.length;
40169 * Resizes all the tabs to the passed width
40170 * @param {Number} The new width
40172 setTabWidth : function(width){
40173 this.currentTabWidth = width;
40174 for(var i = 0, len = this.items.length; i < len; i++) {
40175 if(!this.items[i].isHidden()) {
40176 this.items[i].setWidth(width);
40182 * Destroys this TabPanel
40183 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40185 destroy : function(removeEl){
40186 Roo.EventManager.removeResizeListener(this.onResize, this);
40187 for(var i = 0, len = this.items.length; i < len; i++){
40188 this.items[i].purgeListeners();
40190 if(removeEl === true){
40191 this.el.update("");
40196 createStrip : function(container)
40198 var strip = document.createElement("nav");
40199 strip.className = Roo.bootstrap.version == 4 ?
40200 "navbar-light bg-light" :
40201 "navbar navbar-default"; //"x-tabs-wrap";
40202 container.appendChild(strip);
40206 createStripList : function(strip)
40208 // div wrapper for retard IE
40209 // returns the "tr" element.
40210 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40211 //'<div class="x-tabs-strip-wrap">'+
40212 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40213 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40214 return strip.firstChild; //.firstChild.firstChild.firstChild;
40216 createBody : function(container)
40218 var body = document.createElement("div");
40219 Roo.id(body, "tab-body");
40220 //Roo.fly(body).addClass("x-tabs-body");
40221 Roo.fly(body).addClass("tab-content");
40222 container.appendChild(body);
40225 createItemBody :function(bodyEl, id){
40226 var body = Roo.getDom(id);
40228 body = document.createElement("div");
40231 //Roo.fly(body).addClass("x-tabs-item-body");
40232 Roo.fly(body).addClass("tab-pane");
40233 bodyEl.insertBefore(body, bodyEl.firstChild);
40237 createStripElements : function(stripEl, text, closable, tpl)
40239 var td = document.createElement("li"); // was td..
40240 td.className = 'nav-item';
40242 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40245 stripEl.appendChild(td);
40247 td.className = "x-tabs-closable";
40248 if(!this.closeTpl){
40249 this.closeTpl = new Roo.Template(
40250 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40251 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40252 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40255 var el = this.closeTpl.overwrite(td, {"text": text});
40256 var close = el.getElementsByTagName("div")[0];
40257 var inner = el.getElementsByTagName("em")[0];
40258 return {"el": el, "close": close, "inner": inner};
40261 // not sure what this is..
40262 // if(!this.tabTpl){
40263 //this.tabTpl = new Roo.Template(
40264 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40265 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40267 // this.tabTpl = new Roo.Template(
40268 // '<a href="#">' +
40269 // '<span unselectable="on"' +
40270 // (this.disableTooltips ? '' : ' title="{text}"') +
40271 // ' >{text}</span></a>'
40277 var template = tpl || this.tabTpl || false;
40280 template = new Roo.Template(
40281 Roo.bootstrap.version == 4 ?
40283 '<a class="nav-link" href="#" unselectable="on"' +
40284 (this.disableTooltips ? '' : ' title="{text}"') +
40287 '<a class="nav-link" href="#">' +
40288 '<span unselectable="on"' +
40289 (this.disableTooltips ? '' : ' title="{text}"') +
40290 ' >{text}</span></a>'
40295 switch (typeof(template)) {
40299 template = new Roo.Template(template);
40305 var el = template.overwrite(td, {"text": text});
40307 var inner = el.getElementsByTagName("span")[0];
40309 return {"el": el, "inner": inner};
40317 * @class Roo.TabPanelItem
40318 * @extends Roo.util.Observable
40319 * Represents an individual item (tab plus body) in a TabPanel.
40320 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40321 * @param {String} id The id of this TabPanelItem
40322 * @param {String} text The text for the tab of this TabPanelItem
40323 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40325 Roo.bootstrap.panel.TabItem = function(config){
40327 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40328 * @type Roo.TabPanel
40330 this.tabPanel = config.panel;
40332 * The id for this TabPanelItem
40335 this.id = config.id;
40337 this.disabled = false;
40339 this.text = config.text;
40341 this.loaded = false;
40342 this.closable = config.closable;
40345 * The body element for this TabPanelItem.
40346 * @type Roo.Element
40348 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40349 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40350 this.bodyEl.setStyle("display", "block");
40351 this.bodyEl.setStyle("zoom", "1");
40352 //this.hideAction();
40354 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40356 this.el = Roo.get(els.el);
40357 this.inner = Roo.get(els.inner, true);
40358 this.textEl = Roo.bootstrap.version == 4 ?
40359 this.el : Roo.get(this.el.dom.firstChild, true);
40361 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40362 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40365 // this.el.on("mousedown", this.onTabMouseDown, this);
40366 this.el.on("click", this.onTabClick, this);
40368 if(config.closable){
40369 var c = Roo.get(els.close, true);
40370 c.dom.title = this.closeText;
40371 c.addClassOnOver("close-over");
40372 c.on("click", this.closeClick, this);
40378 * Fires when this tab becomes the active tab.
40379 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40380 * @param {Roo.TabPanelItem} this
40384 * @event beforeclose
40385 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40386 * @param {Roo.TabPanelItem} this
40387 * @param {Object} e Set cancel to true on this object to cancel the close.
40389 "beforeclose": true,
40392 * Fires when this tab is closed.
40393 * @param {Roo.TabPanelItem} this
40397 * @event deactivate
40398 * Fires when this tab is no longer the active tab.
40399 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40400 * @param {Roo.TabPanelItem} this
40402 "deactivate" : true
40404 this.hidden = false;
40406 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40409 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40411 purgeListeners : function(){
40412 Roo.util.Observable.prototype.purgeListeners.call(this);
40413 this.el.removeAllListeners();
40416 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40419 this.status_node.addClass("active");
40422 this.tabPanel.stripWrap.repaint();
40424 this.fireEvent("activate", this.tabPanel, this);
40428 * Returns true if this tab is the active tab.
40429 * @return {Boolean}
40431 isActive : function(){
40432 return this.tabPanel.getActiveTab() == this;
40436 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40439 this.status_node.removeClass("active");
40441 this.fireEvent("deactivate", this.tabPanel, this);
40444 hideAction : function(){
40445 this.bodyEl.hide();
40446 this.bodyEl.setStyle("position", "absolute");
40447 this.bodyEl.setLeft("-20000px");
40448 this.bodyEl.setTop("-20000px");
40451 showAction : function(){
40452 this.bodyEl.setStyle("position", "relative");
40453 this.bodyEl.setTop("");
40454 this.bodyEl.setLeft("");
40455 this.bodyEl.show();
40459 * Set the tooltip for the tab.
40460 * @param {String} tooltip The tab's tooltip
40462 setTooltip : function(text){
40463 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40464 this.textEl.dom.qtip = text;
40465 this.textEl.dom.removeAttribute('title');
40467 this.textEl.dom.title = text;
40471 onTabClick : function(e){
40472 e.preventDefault();
40473 this.tabPanel.activate(this.id);
40476 onTabMouseDown : function(e){
40477 e.preventDefault();
40478 this.tabPanel.activate(this.id);
40481 getWidth : function(){
40482 return this.inner.getWidth();
40485 setWidth : function(width){
40486 var iwidth = width - this.linode.getPadding("lr");
40487 this.inner.setWidth(iwidth);
40488 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40489 this.linode.setWidth(width);
40493 * Show or hide the tab
40494 * @param {Boolean} hidden True to hide or false to show.
40496 setHidden : function(hidden){
40497 this.hidden = hidden;
40498 this.linode.setStyle("display", hidden ? "none" : "");
40502 * Returns true if this tab is "hidden"
40503 * @return {Boolean}
40505 isHidden : function(){
40506 return this.hidden;
40510 * Returns the text for this tab
40513 getText : function(){
40517 autoSize : function(){
40518 //this.el.beginMeasure();
40519 this.textEl.setWidth(1);
40521 * #2804 [new] Tabs in Roojs
40522 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40524 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40525 //this.el.endMeasure();
40529 * Sets the text for the tab (Note: this also sets the tooltip text)
40530 * @param {String} text The tab's text and tooltip
40532 setText : function(text){
40534 this.textEl.update(text);
40535 this.setTooltip(text);
40536 //if(!this.tabPanel.resizeTabs){
40537 // this.autoSize();
40541 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40543 activate : function(){
40544 this.tabPanel.activate(this.id);
40548 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40550 disable : function(){
40551 if(this.tabPanel.active != this){
40552 this.disabled = true;
40553 this.status_node.addClass("disabled");
40558 * Enables this TabPanelItem if it was previously disabled.
40560 enable : function(){
40561 this.disabled = false;
40562 this.status_node.removeClass("disabled");
40566 * Sets the content for this TabPanelItem.
40567 * @param {String} content The content
40568 * @param {Boolean} loadScripts true to look for and load scripts
40570 setContent : function(content, loadScripts){
40571 this.bodyEl.update(content, loadScripts);
40575 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40576 * @return {Roo.UpdateManager} The UpdateManager
40578 getUpdateManager : function(){
40579 return this.bodyEl.getUpdateManager();
40583 * Set a URL to be used to load the content for this TabPanelItem.
40584 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40585 * @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)
40586 * @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)
40587 * @return {Roo.UpdateManager} The UpdateManager
40589 setUrl : function(url, params, loadOnce){
40590 if(this.refreshDelegate){
40591 this.un('activate', this.refreshDelegate);
40593 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40594 this.on("activate", this.refreshDelegate);
40595 return this.bodyEl.getUpdateManager();
40599 _handleRefresh : function(url, params, loadOnce){
40600 if(!loadOnce || !this.loaded){
40601 var updater = this.bodyEl.getUpdateManager();
40602 updater.update(url, params, this._setLoaded.createDelegate(this));
40607 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40608 * Will fail silently if the setUrl method has not been called.
40609 * This does not activate the panel, just updates its content.
40611 refresh : function(){
40612 if(this.refreshDelegate){
40613 this.loaded = false;
40614 this.refreshDelegate();
40619 _setLoaded : function(){
40620 this.loaded = true;
40624 closeClick : function(e){
40627 this.fireEvent("beforeclose", this, o);
40628 if(o.cancel !== true){
40629 this.tabPanel.removeTab(this.id);
40633 * The text displayed in the tooltip for the close icon.
40636 closeText : "Close this tab"
40639 * This script refer to:
40640 * Title: International Telephone Input
40641 * Author: Jack O'Connor
40642 * Code version: v12.1.12
40643 * Availability: https://github.com/jackocnr/intl-tel-input.git
40646 Roo.bootstrap.PhoneInputData = function() {
40649 "Afghanistan (افغانستان)",
40654 "Albania (Shqipëri)",
40659 "Algeria (الجزائر)",
40684 "Antigua and Barbuda",
40694 "Armenia (Հայաստան)",
40710 "Austria (Österreich)",
40715 "Azerbaijan (Azərbaycan)",
40725 "Bahrain (البحرين)",
40730 "Bangladesh (বাংলাদেশ)",
40740 "Belarus (Беларусь)",
40745 "Belgium (België)",
40775 "Bosnia and Herzegovina (Босна и Херцеговина)",
40790 "British Indian Ocean Territory",
40795 "British Virgin Islands",
40805 "Bulgaria (България)",
40815 "Burundi (Uburundi)",
40820 "Cambodia (កម្ពុជា)",
40825 "Cameroon (Cameroun)",
40834 ["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"]
40837 "Cape Verde (Kabu Verdi)",
40842 "Caribbean Netherlands",
40853 "Central African Republic (République centrafricaine)",
40873 "Christmas Island",
40879 "Cocos (Keeling) Islands",
40890 "Comoros (جزر القمر)",
40895 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40900 "Congo (Republic) (Congo-Brazzaville)",
40920 "Croatia (Hrvatska)",
40941 "Czech Republic (Česká republika)",
40946 "Denmark (Danmark)",
40961 "Dominican Republic (República Dominicana)",
40965 ["809", "829", "849"]
40983 "Equatorial Guinea (Guinea Ecuatorial)",
41003 "Falkland Islands (Islas Malvinas)",
41008 "Faroe Islands (Føroyar)",
41029 "French Guiana (Guyane française)",
41034 "French Polynesia (Polynésie française)",
41049 "Georgia (საქართველო)",
41054 "Germany (Deutschland)",
41074 "Greenland (Kalaallit Nunaat)",
41111 "Guinea-Bissau (Guiné Bissau)",
41136 "Hungary (Magyarország)",
41141 "Iceland (Ísland)",
41161 "Iraq (العراق)",
41177 "Israel (ישראל)",
41204 "Jordan (الأردن)",
41209 "Kazakhstan (Казахстан)",
41230 "Kuwait (الكويت)",
41235 "Kyrgyzstan (Кыргызстан)",
41245 "Latvia (Latvija)",
41250 "Lebanon (لبنان)",
41265 "Libya (ليبيا)",
41275 "Lithuania (Lietuva)",
41290 "Macedonia (FYROM) (Македонија)",
41295 "Madagascar (Madagasikara)",
41325 "Marshall Islands",
41335 "Mauritania (موريتانيا)",
41340 "Mauritius (Moris)",
41361 "Moldova (Republica Moldova)",
41371 "Mongolia (Монгол)",
41376 "Montenegro (Crna Gora)",
41386 "Morocco (المغرب)",
41392 "Mozambique (Moçambique)",
41397 "Myanmar (Burma) (မြန်မာ)",
41402 "Namibia (Namibië)",
41417 "Netherlands (Nederland)",
41422 "New Caledonia (Nouvelle-Calédonie)",
41457 "North Korea (조선 민주주의 인민 공화국)",
41462 "Northern Mariana Islands",
41478 "Pakistan (پاکستان)",
41488 "Palestine (فلسطين)",
41498 "Papua New Guinea",
41540 "Réunion (La Réunion)",
41546 "Romania (România)",
41562 "Saint Barthélemy",
41573 "Saint Kitts and Nevis",
41583 "Saint Martin (Saint-Martin (partie française))",
41589 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41594 "Saint Vincent and the Grenadines",
41609 "São Tomé and Príncipe (São Tomé e Príncipe)",
41614 "Saudi Arabia (المملكة العربية السعودية)",
41619 "Senegal (Sénégal)",
41649 "Slovakia (Slovensko)",
41654 "Slovenia (Slovenija)",
41664 "Somalia (Soomaaliya)",
41674 "South Korea (대한민국)",
41679 "South Sudan (جنوب السودان)",
41689 "Sri Lanka (ශ්රී ලංකාව)",
41694 "Sudan (السودان)",
41704 "Svalbard and Jan Mayen",
41715 "Sweden (Sverige)",
41720 "Switzerland (Schweiz)",
41725 "Syria (سوريا)",
41770 "Trinidad and Tobago",
41775 "Tunisia (تونس)",
41780 "Turkey (Türkiye)",
41790 "Turks and Caicos Islands",
41800 "U.S. Virgin Islands",
41810 "Ukraine (Україна)",
41815 "United Arab Emirates (الإمارات العربية المتحدة)",
41837 "Uzbekistan (Oʻzbekiston)",
41847 "Vatican City (Città del Vaticano)",
41858 "Vietnam (Việt Nam)",
41863 "Wallis and Futuna (Wallis-et-Futuna)",
41868 "Western Sahara (الصحراء الغربية)",
41874 "Yemen (اليمن)",
41898 * This script refer to:
41899 * Title: International Telephone Input
41900 * Author: Jack O'Connor
41901 * Code version: v12.1.12
41902 * Availability: https://github.com/jackocnr/intl-tel-input.git
41906 * @class Roo.bootstrap.PhoneInput
41907 * @extends Roo.bootstrap.TriggerField
41908 * An input with International dial-code selection
41910 * @cfg {String} defaultDialCode default '+852'
41911 * @cfg {Array} preferedCountries default []
41914 * Create a new PhoneInput.
41915 * @param {Object} config Configuration options
41918 Roo.bootstrap.PhoneInput = function(config) {
41919 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41922 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41924 listWidth: undefined,
41926 selectedClass: 'active',
41928 invalidClass : "has-warning",
41930 validClass: 'has-success',
41932 allowed: '0123456789',
41937 * @cfg {String} defaultDialCode The default dial code when initializing the input
41939 defaultDialCode: '+852',
41942 * @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
41944 preferedCountries: false,
41946 getAutoCreate : function()
41948 var data = Roo.bootstrap.PhoneInputData();
41949 var align = this.labelAlign || this.parentLabelAlign();
41952 this.allCountries = [];
41953 this.dialCodeMapping = [];
41955 for (var i = 0; i < data.length; i++) {
41957 this.allCountries[i] = {
41961 priority: c[3] || 0,
41962 areaCodes: c[4] || null
41964 this.dialCodeMapping[c[2]] = {
41967 priority: c[3] || 0,
41968 areaCodes: c[4] || null
41980 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41981 maxlength: this.max_length,
41982 cls : 'form-control tel-input',
41983 autocomplete: 'new-password'
41986 var hiddenInput = {
41989 cls: 'hidden-tel-input'
41993 hiddenInput.name = this.name;
41996 if (this.disabled) {
41997 input.disabled = true;
42000 var flag_container = {
42017 cls: this.hasFeedback ? 'has-feedback' : '',
42023 cls: 'dial-code-holder',
42030 cls: 'roo-select2-container input-group',
42037 if (this.fieldLabel.length) {
42040 tooltip: 'This field is required'
42046 cls: 'control-label',
42052 html: this.fieldLabel
42055 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42061 if(this.indicatorpos == 'right') {
42062 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42069 if(align == 'left') {
42077 if(this.labelWidth > 12){
42078 label.style = "width: " + this.labelWidth + 'px';
42080 if(this.labelWidth < 13 && this.labelmd == 0){
42081 this.labelmd = this.labelWidth;
42083 if(this.labellg > 0){
42084 label.cls += ' col-lg-' + this.labellg;
42085 input.cls += ' col-lg-' + (12 - this.labellg);
42087 if(this.labelmd > 0){
42088 label.cls += ' col-md-' + this.labelmd;
42089 container.cls += ' col-md-' + (12 - this.labelmd);
42091 if(this.labelsm > 0){
42092 label.cls += ' col-sm-' + this.labelsm;
42093 container.cls += ' col-sm-' + (12 - this.labelsm);
42095 if(this.labelxs > 0){
42096 label.cls += ' col-xs-' + this.labelxs;
42097 container.cls += ' col-xs-' + (12 - this.labelxs);
42107 var settings = this;
42109 ['xs','sm','md','lg'].map(function(size){
42110 if (settings[size]) {
42111 cfg.cls += ' col-' + size + '-' + settings[size];
42115 this.store = new Roo.data.Store({
42116 proxy : new Roo.data.MemoryProxy({}),
42117 reader : new Roo.data.JsonReader({
42128 'name' : 'dialCode',
42132 'name' : 'priority',
42136 'name' : 'areaCodes',
42143 if(!this.preferedCountries) {
42144 this.preferedCountries = [
42151 var p = this.preferedCountries.reverse();
42154 for (var i = 0; i < p.length; i++) {
42155 for (var j = 0; j < this.allCountries.length; j++) {
42156 if(this.allCountries[j].iso2 == p[i]) {
42157 var t = this.allCountries[j];
42158 this.allCountries.splice(j,1);
42159 this.allCountries.unshift(t);
42165 this.store.proxy.data = {
42167 data: this.allCountries
42173 initEvents : function()
42176 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42178 this.indicator = this.indicatorEl();
42179 this.flag = this.flagEl();
42180 this.dialCodeHolder = this.dialCodeHolderEl();
42182 this.trigger = this.el.select('div.flag-box',true).first();
42183 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42188 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42189 _this.list.setWidth(lw);
42192 this.list.on('mouseover', this.onViewOver, this);
42193 this.list.on('mousemove', this.onViewMove, this);
42194 this.inputEl().on("keyup", this.onKeyUp, this);
42195 this.inputEl().on("keypress", this.onKeyPress, this);
42197 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42199 this.view = new Roo.View(this.list, this.tpl, {
42200 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42203 this.view.on('click', this.onViewClick, this);
42204 this.setValue(this.defaultDialCode);
42207 onTriggerClick : function(e)
42209 Roo.log('trigger click');
42214 if(this.isExpanded()){
42216 this.hasFocus = false;
42218 this.store.load({});
42219 this.hasFocus = true;
42224 isExpanded : function()
42226 return this.list.isVisible();
42229 collapse : function()
42231 if(!this.isExpanded()){
42235 Roo.get(document).un('mousedown', this.collapseIf, this);
42236 Roo.get(document).un('mousewheel', this.collapseIf, this);
42237 this.fireEvent('collapse', this);
42241 expand : function()
42245 if(this.isExpanded() || !this.hasFocus){
42249 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42250 this.list.setWidth(lw);
42253 this.restrictHeight();
42255 Roo.get(document).on('mousedown', this.collapseIf, this);
42256 Roo.get(document).on('mousewheel', this.collapseIf, this);
42258 this.fireEvent('expand', this);
42261 restrictHeight : function()
42263 this.list.alignTo(this.inputEl(), this.listAlign);
42264 this.list.alignTo(this.inputEl(), this.listAlign);
42267 onViewOver : function(e, t)
42269 if(this.inKeyMode){
42272 var item = this.view.findItemFromChild(t);
42275 var index = this.view.indexOf(item);
42276 this.select(index, false);
42281 onViewClick : function(view, doFocus, el, e)
42283 var index = this.view.getSelectedIndexes()[0];
42285 var r = this.store.getAt(index);
42288 this.onSelect(r, index);
42290 if(doFocus !== false && !this.blockFocus){
42291 this.inputEl().focus();
42295 onViewMove : function(e, t)
42297 this.inKeyMode = false;
42300 select : function(index, scrollIntoView)
42302 this.selectedIndex = index;
42303 this.view.select(index);
42304 if(scrollIntoView !== false){
42305 var el = this.view.getNode(index);
42307 this.list.scrollChildIntoView(el, false);
42312 createList : function()
42314 this.list = Roo.get(document.body).createChild({
42316 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42317 style: 'display:none'
42320 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42323 collapseIf : function(e)
42325 var in_combo = e.within(this.el);
42326 var in_list = e.within(this.list);
42327 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42329 if (in_combo || in_list || is_list) {
42335 onSelect : function(record, index)
42337 if(this.fireEvent('beforeselect', this, record, index) !== false){
42339 this.setFlagClass(record.data.iso2);
42340 this.setDialCode(record.data.dialCode);
42341 this.hasFocus = false;
42343 this.fireEvent('select', this, record, index);
42347 flagEl : function()
42349 var flag = this.el.select('div.flag',true).first();
42356 dialCodeHolderEl : function()
42358 var d = this.el.select('input.dial-code-holder',true).first();
42365 setDialCode : function(v)
42367 this.dialCodeHolder.dom.value = '+'+v;
42370 setFlagClass : function(n)
42372 this.flag.dom.className = 'flag '+n;
42375 getValue : function()
42377 var v = this.inputEl().getValue();
42378 if(this.dialCodeHolder) {
42379 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42384 setValue : function(v)
42386 var d = this.getDialCode(v);
42388 //invalid dial code
42389 if(v.length == 0 || !d || d.length == 0) {
42391 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42392 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42398 this.setFlagClass(this.dialCodeMapping[d].iso2);
42399 this.setDialCode(d);
42400 this.inputEl().dom.value = v.replace('+'+d,'');
42401 this.hiddenEl().dom.value = this.getValue();
42406 getDialCode : function(v)
42410 if (v.length == 0) {
42411 return this.dialCodeHolder.dom.value;
42415 if (v.charAt(0) != "+") {
42418 var numericChars = "";
42419 for (var i = 1; i < v.length; i++) {
42420 var c = v.charAt(i);
42423 if (this.dialCodeMapping[numericChars]) {
42424 dialCode = v.substr(1, i);
42426 if (numericChars.length == 4) {
42436 this.setValue(this.defaultDialCode);
42440 hiddenEl : function()
42442 return this.el.select('input.hidden-tel-input',true).first();
42445 // after setting val
42446 onKeyUp : function(e){
42447 this.setValue(this.getValue());
42450 onKeyPress : function(e){
42451 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42458 * @class Roo.bootstrap.MoneyField
42459 * @extends Roo.bootstrap.ComboBox
42460 * Bootstrap MoneyField class
42463 * Create a new MoneyField.
42464 * @param {Object} config Configuration options
42467 Roo.bootstrap.MoneyField = function(config) {
42469 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42473 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42476 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42478 allowDecimals : true,
42480 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42482 decimalSeparator : ".",
42484 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42486 decimalPrecision : 0,
42488 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42490 allowNegative : true,
42492 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42496 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42498 minValue : Number.NEGATIVE_INFINITY,
42500 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42502 maxValue : Number.MAX_VALUE,
42504 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42506 minText : "The minimum value for this field is {0}",
42508 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42510 maxText : "The maximum value for this field is {0}",
42512 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42513 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42515 nanText : "{0} is not a valid number",
42517 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42521 * @cfg {String} defaults currency of the MoneyField
42522 * value should be in lkey
42524 defaultCurrency : false,
42526 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42528 thousandsDelimiter : false,
42530 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42541 getAutoCreate : function()
42543 var align = this.labelAlign || this.parentLabelAlign();
42555 cls : 'form-control roo-money-amount-input',
42556 autocomplete: 'new-password'
42559 var hiddenInput = {
42563 cls: 'hidden-number-input'
42566 if(this.max_length) {
42567 input.maxlength = this.max_length;
42571 hiddenInput.name = this.name;
42574 if (this.disabled) {
42575 input.disabled = true;
42578 var clg = 12 - this.inputlg;
42579 var cmd = 12 - this.inputmd;
42580 var csm = 12 - this.inputsm;
42581 var cxs = 12 - this.inputxs;
42585 cls : 'row roo-money-field',
42589 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42593 cls: 'roo-select2-container input-group',
42597 cls : 'form-control roo-money-currency-input',
42598 autocomplete: 'new-password',
42600 name : this.currencyName
42604 cls : 'input-group-addon',
42618 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42622 cls: this.hasFeedback ? 'has-feedback' : '',
42633 if (this.fieldLabel.length) {
42636 tooltip: 'This field is required'
42642 cls: 'control-label',
42648 html: this.fieldLabel
42651 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42657 if(this.indicatorpos == 'right') {
42658 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42665 if(align == 'left') {
42673 if(this.labelWidth > 12){
42674 label.style = "width: " + this.labelWidth + 'px';
42676 if(this.labelWidth < 13 && this.labelmd == 0){
42677 this.labelmd = this.labelWidth;
42679 if(this.labellg > 0){
42680 label.cls += ' col-lg-' + this.labellg;
42681 input.cls += ' col-lg-' + (12 - this.labellg);
42683 if(this.labelmd > 0){
42684 label.cls += ' col-md-' + this.labelmd;
42685 container.cls += ' col-md-' + (12 - this.labelmd);
42687 if(this.labelsm > 0){
42688 label.cls += ' col-sm-' + this.labelsm;
42689 container.cls += ' col-sm-' + (12 - this.labelsm);
42691 if(this.labelxs > 0){
42692 label.cls += ' col-xs-' + this.labelxs;
42693 container.cls += ' col-xs-' + (12 - this.labelxs);
42704 var settings = this;
42706 ['xs','sm','md','lg'].map(function(size){
42707 if (settings[size]) {
42708 cfg.cls += ' col-' + size + '-' + settings[size];
42715 initEvents : function()
42717 this.indicator = this.indicatorEl();
42719 this.initCurrencyEvent();
42721 this.initNumberEvent();
42724 initCurrencyEvent : function()
42727 throw "can not find store for combo";
42730 this.store = Roo.factory(this.store, Roo.data);
42731 this.store.parent = this;
42735 this.triggerEl = this.el.select('.input-group-addon', true).first();
42737 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42742 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42743 _this.list.setWidth(lw);
42746 this.list.on('mouseover', this.onViewOver, this);
42747 this.list.on('mousemove', this.onViewMove, this);
42748 this.list.on('scroll', this.onViewScroll, this);
42751 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42754 this.view = new Roo.View(this.list, this.tpl, {
42755 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42758 this.view.on('click', this.onViewClick, this);
42760 this.store.on('beforeload', this.onBeforeLoad, this);
42761 this.store.on('load', this.onLoad, this);
42762 this.store.on('loadexception', this.onLoadException, this);
42764 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42765 "up" : function(e){
42766 this.inKeyMode = true;
42770 "down" : function(e){
42771 if(!this.isExpanded()){
42772 this.onTriggerClick();
42774 this.inKeyMode = true;
42779 "enter" : function(e){
42782 if(this.fireEvent("specialkey", this, e)){
42783 this.onViewClick(false);
42789 "esc" : function(e){
42793 "tab" : function(e){
42796 if(this.fireEvent("specialkey", this, e)){
42797 this.onViewClick(false);
42805 doRelay : function(foo, bar, hname){
42806 if(hname == 'down' || this.scope.isExpanded()){
42807 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42815 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42819 initNumberEvent : function(e)
42821 this.inputEl().on("keydown" , this.fireKey, this);
42822 this.inputEl().on("focus", this.onFocus, this);
42823 this.inputEl().on("blur", this.onBlur, this);
42825 this.inputEl().relayEvent('keyup', this);
42827 if(this.indicator){
42828 this.indicator.addClass('invisible');
42831 this.originalValue = this.getValue();
42833 if(this.validationEvent == 'keyup'){
42834 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42835 this.inputEl().on('keyup', this.filterValidation, this);
42837 else if(this.validationEvent !== false){
42838 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42841 if(this.selectOnFocus){
42842 this.on("focus", this.preFocus, this);
42845 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42846 this.inputEl().on("keypress", this.filterKeys, this);
42848 this.inputEl().relayEvent('keypress', this);
42851 var allowed = "0123456789";
42853 if(this.allowDecimals){
42854 allowed += this.decimalSeparator;
42857 if(this.allowNegative){
42861 if(this.thousandsDelimiter) {
42865 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42867 var keyPress = function(e){
42869 var k = e.getKey();
42871 var c = e.getCharCode();
42874 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42875 allowed.indexOf(String.fromCharCode(c)) === -1
42881 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42885 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42890 this.inputEl().on("keypress", keyPress, this);
42894 onTriggerClick : function(e)
42901 this.loadNext = false;
42903 if(this.isExpanded()){
42908 this.hasFocus = true;
42910 if(this.triggerAction == 'all') {
42911 this.doQuery(this.allQuery, true);
42915 this.doQuery(this.getRawValue());
42918 getCurrency : function()
42920 var v = this.currencyEl().getValue();
42925 restrictHeight : function()
42927 this.list.alignTo(this.currencyEl(), this.listAlign);
42928 this.list.alignTo(this.currencyEl(), this.listAlign);
42931 onViewClick : function(view, doFocus, el, e)
42933 var index = this.view.getSelectedIndexes()[0];
42935 var r = this.store.getAt(index);
42938 this.onSelect(r, index);
42942 onSelect : function(record, index){
42944 if(this.fireEvent('beforeselect', this, record, index) !== false){
42946 this.setFromCurrencyData(index > -1 ? record.data : false);
42950 this.fireEvent('select', this, record, index);
42954 setFromCurrencyData : function(o)
42958 this.lastCurrency = o;
42960 if (this.currencyField) {
42961 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42963 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
42966 this.lastSelectionText = currency;
42968 //setting default currency
42969 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42970 this.setCurrency(this.defaultCurrency);
42974 this.setCurrency(currency);
42977 setFromData : function(o)
42981 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42983 this.setFromCurrencyData(c);
42988 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42990 Roo.log('no value set for '+ (this.name ? this.name : this.id));
42993 this.setValue(value);
42997 setCurrency : function(v)
42999 this.currencyValue = v;
43002 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43007 setValue : function(v)
43009 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43015 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43017 this.inputEl().dom.value = (v == '') ? '' :
43018 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43020 if(!this.allowZero && v === '0') {
43021 this.hiddenEl().dom.value = '';
43022 this.inputEl().dom.value = '';
43029 getRawValue : function()
43031 var v = this.inputEl().getValue();
43036 getValue : function()
43038 return this.fixPrecision(this.parseValue(this.getRawValue()));
43041 parseValue : function(value)
43043 if(this.thousandsDelimiter) {
43045 r = new RegExp(",", "g");
43046 value = value.replace(r, "");
43049 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43050 return isNaN(value) ? '' : value;
43054 fixPrecision : function(value)
43056 if(this.thousandsDelimiter) {
43058 r = new RegExp(",", "g");
43059 value = value.replace(r, "");
43062 var nan = isNaN(value);
43064 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43065 return nan ? '' : value;
43067 return parseFloat(value).toFixed(this.decimalPrecision);
43070 decimalPrecisionFcn : function(v)
43072 return Math.floor(v);
43075 validateValue : function(value)
43077 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43081 var num = this.parseValue(value);
43084 this.markInvalid(String.format(this.nanText, value));
43088 if(num < this.minValue){
43089 this.markInvalid(String.format(this.minText, this.minValue));
43093 if(num > this.maxValue){
43094 this.markInvalid(String.format(this.maxText, this.maxValue));
43101 validate : function()
43103 if(this.disabled || this.allowBlank){
43108 var currency = this.getCurrency();
43110 if(this.validateValue(this.getRawValue()) && currency.length){
43115 this.markInvalid();
43119 getName: function()
43124 beforeBlur : function()
43130 var v = this.parseValue(this.getRawValue());
43137 onBlur : function()
43141 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43142 //this.el.removeClass(this.focusClass);
43145 this.hasFocus = false;
43147 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43151 var v = this.getValue();
43153 if(String(v) !== String(this.startValue)){
43154 this.fireEvent('change', this, v, this.startValue);
43157 this.fireEvent("blur", this);
43160 inputEl : function()
43162 return this.el.select('.roo-money-amount-input', true).first();
43165 currencyEl : function()
43167 return this.el.select('.roo-money-currency-input', true).first();
43170 hiddenEl : function()
43172 return this.el.select('input.hidden-number-input',true).first();
43176 * @class Roo.bootstrap.BezierSignature
43177 * @extends Roo.bootstrap.Component
43178 * Bootstrap BezierSignature class
43179 * This script refer to:
43180 * Title: Signature Pad
43182 * Availability: https://github.com/szimek/signature_pad
43185 * Create a new BezierSignature
43186 * @param {Object} config The config object
43189 Roo.bootstrap.BezierSignature = function(config){
43190 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43196 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43203 mouse_btn_down: true,
43206 * @cfg {int} canvas height
43208 canvas_height: '200px',
43211 * @cfg {float|function} Radius of a single dot.
43216 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43221 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43226 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43231 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43236 * @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.
43238 bg_color: 'rgba(0, 0, 0, 0)',
43241 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43243 dot_color: 'black',
43246 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43248 velocity_filter_weight: 0.7,
43251 * @cfg {function} Callback when stroke begin.
43256 * @cfg {function} Callback when stroke end.
43260 getAutoCreate : function()
43262 var cls = 'roo-signature column';
43265 cls += ' ' + this.cls;
43275 for(var i = 0; i < col_sizes.length; i++) {
43276 if(this[col_sizes[i]]) {
43277 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43287 cls: 'roo-signature-body',
43291 cls: 'roo-signature-body-canvas',
43292 height: this.canvas_height,
43293 width: this.canvas_width
43300 style: 'display: none'
43308 initEvents: function()
43310 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43312 var canvas = this.canvasEl();
43314 // mouse && touch event swapping...
43315 canvas.dom.style.touchAction = 'none';
43316 canvas.dom.style.msTouchAction = 'none';
43318 this.mouse_btn_down = false;
43319 canvas.on('mousedown', this._handleMouseDown, this);
43320 canvas.on('mousemove', this._handleMouseMove, this);
43321 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43323 if (window.PointerEvent) {
43324 canvas.on('pointerdown', this._handleMouseDown, this);
43325 canvas.on('pointermove', this._handleMouseMove, this);
43326 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43329 if ('ontouchstart' in window) {
43330 canvas.on('touchstart', this._handleTouchStart, this);
43331 canvas.on('touchmove', this._handleTouchMove, this);
43332 canvas.on('touchend', this._handleTouchEnd, this);
43335 Roo.EventManager.onWindowResize(this.resize, this, true);
43337 // file input event
43338 this.fileEl().on('change', this.uploadImage, this);
43345 resize: function(){
43347 var canvas = this.canvasEl().dom;
43348 var ctx = this.canvasElCtx();
43349 var img_data = false;
43351 if(canvas.width > 0) {
43352 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43354 // setting canvas width will clean img data
43357 var style = window.getComputedStyle ?
43358 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43360 var padding_left = parseInt(style.paddingLeft) || 0;
43361 var padding_right = parseInt(style.paddingRight) || 0;
43363 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43366 ctx.putImageData(img_data, 0, 0);
43370 _handleMouseDown: function(e)
43372 if (e.browserEvent.which === 1) {
43373 this.mouse_btn_down = true;
43374 this.strokeBegin(e);
43378 _handleMouseMove: function (e)
43380 if (this.mouse_btn_down) {
43381 this.strokeMoveUpdate(e);
43385 _handleMouseUp: function (e)
43387 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43388 this.mouse_btn_down = false;
43393 _handleTouchStart: function (e) {
43395 e.preventDefault();
43396 if (e.browserEvent.targetTouches.length === 1) {
43397 // var touch = e.browserEvent.changedTouches[0];
43398 // this.strokeBegin(touch);
43400 this.strokeBegin(e); // assume e catching the correct xy...
43404 _handleTouchMove: function (e) {
43405 e.preventDefault();
43406 // var touch = event.targetTouches[0];
43407 // _this._strokeMoveUpdate(touch);
43408 this.strokeMoveUpdate(e);
43411 _handleTouchEnd: function (e) {
43412 var wasCanvasTouched = e.target === this.canvasEl().dom;
43413 if (wasCanvasTouched) {
43414 e.preventDefault();
43415 // var touch = event.changedTouches[0];
43416 // _this._strokeEnd(touch);
43421 reset: function () {
43422 this._lastPoints = [];
43423 this._lastVelocity = 0;
43424 this._lastWidth = (this.min_width + this.max_width) / 2;
43425 this.canvasElCtx().fillStyle = this.dot_color;
43428 strokeMoveUpdate: function(e)
43430 this.strokeUpdate(e);
43432 if (this.throttle) {
43433 this.throttleStroke(this.strokeUpdate, this.throttle);
43436 this.strokeUpdate(e);
43440 strokeBegin: function(e)
43442 var newPointGroup = {
43443 color: this.dot_color,
43447 if (typeof this.onBegin === 'function') {
43451 this.curve_data.push(newPointGroup);
43453 this.strokeUpdate(e);
43456 strokeUpdate: function(e)
43458 var rect = this.canvasEl().dom.getBoundingClientRect();
43459 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43460 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43461 var lastPoints = lastPointGroup.points;
43462 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43463 var isLastPointTooClose = lastPoint
43464 ? point.distanceTo(lastPoint) <= this.min_distance
43466 var color = lastPointGroup.color;
43467 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43468 var curve = this.addPoint(point);
43470 this.drawDot({color: color, point: point});
43473 this.drawCurve({color: color, curve: curve});
43483 strokeEnd: function(e)
43485 this.strokeUpdate(e);
43486 if (typeof this.onEnd === 'function') {
43491 addPoint: function (point) {
43492 var _lastPoints = this._lastPoints;
43493 _lastPoints.push(point);
43494 if (_lastPoints.length > 2) {
43495 if (_lastPoints.length === 3) {
43496 _lastPoints.unshift(_lastPoints[0]);
43498 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43499 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43500 _lastPoints.shift();
43506 calculateCurveWidths: function (startPoint, endPoint) {
43507 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43508 (1 - this.velocity_filter_weight) * this._lastVelocity;
43510 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43513 start: this._lastWidth
43516 this._lastVelocity = velocity;
43517 this._lastWidth = newWidth;
43521 drawDot: function (_a) {
43522 var color = _a.color, point = _a.point;
43523 var ctx = this.canvasElCtx();
43524 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43526 this.drawCurveSegment(point.x, point.y, width);
43528 ctx.fillStyle = color;
43532 drawCurve: function (_a) {
43533 var color = _a.color, curve = _a.curve;
43534 var ctx = this.canvasElCtx();
43535 var widthDelta = curve.endWidth - curve.startWidth;
43536 var drawSteps = Math.floor(curve.length()) * 2;
43538 ctx.fillStyle = color;
43539 for (var i = 0; i < drawSteps; i += 1) {
43540 var t = i / drawSteps;
43546 var x = uuu * curve.startPoint.x;
43547 x += 3 * uu * t * curve.control1.x;
43548 x += 3 * u * tt * curve.control2.x;
43549 x += ttt * curve.endPoint.x;
43550 var y = uuu * curve.startPoint.y;
43551 y += 3 * uu * t * curve.control1.y;
43552 y += 3 * u * tt * curve.control2.y;
43553 y += ttt * curve.endPoint.y;
43554 var width = curve.startWidth + ttt * widthDelta;
43555 this.drawCurveSegment(x, y, width);
43561 drawCurveSegment: function (x, y, width) {
43562 var ctx = this.canvasElCtx();
43564 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43565 this.is_empty = false;
43570 var ctx = this.canvasElCtx();
43571 var canvas = this.canvasEl().dom;
43572 ctx.fillStyle = this.bg_color;
43573 ctx.clearRect(0, 0, canvas.width, canvas.height);
43574 ctx.fillRect(0, 0, canvas.width, canvas.height);
43575 this.curve_data = [];
43577 this.is_empty = true;
43582 return this.el.select('input',true).first();
43585 canvasEl: function()
43587 return this.el.select('canvas',true).first();
43590 canvasElCtx: function()
43592 return this.el.select('canvas',true).first().dom.getContext('2d');
43595 getImage: function(type)
43597 if(this.is_empty) {
43602 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43605 drawFromImage: function(img_src)
43607 var img = new Image();
43609 img.onload = function(){
43610 this.canvasElCtx().drawImage(img, 0, 0);
43615 this.is_empty = false;
43618 selectImage: function()
43620 this.fileEl().dom.click();
43623 uploadImage: function(e)
43625 var reader = new FileReader();
43627 reader.onload = function(e){
43628 var img = new Image();
43629 img.onload = function(){
43631 this.canvasElCtx().drawImage(img, 0, 0);
43633 img.src = e.target.result;
43636 reader.readAsDataURL(e.target.files[0]);
43639 // Bezier Point Constructor
43640 Point: (function () {
43641 function Point(x, y, time) {
43644 this.time = time || Date.now();
43646 Point.prototype.distanceTo = function (start) {
43647 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43649 Point.prototype.equals = function (other) {
43650 return this.x === other.x && this.y === other.y && this.time === other.time;
43652 Point.prototype.velocityFrom = function (start) {
43653 return this.time !== start.time
43654 ? this.distanceTo(start) / (this.time - start.time)
43661 // Bezier Constructor
43662 Bezier: (function () {
43663 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43664 this.startPoint = startPoint;
43665 this.control2 = control2;
43666 this.control1 = control1;
43667 this.endPoint = endPoint;
43668 this.startWidth = startWidth;
43669 this.endWidth = endWidth;
43671 Bezier.fromPoints = function (points, widths, scope) {
43672 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43673 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43674 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43676 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43677 var dx1 = s1.x - s2.x;
43678 var dy1 = s1.y - s2.y;
43679 var dx2 = s2.x - s3.x;
43680 var dy2 = s2.y - s3.y;
43681 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43682 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43683 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43684 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43685 var dxm = m1.x - m2.x;
43686 var dym = m1.y - m2.y;
43687 var k = l2 / (l1 + l2);
43688 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43689 var tx = s2.x - cm.x;
43690 var ty = s2.y - cm.y;
43692 c1: new scope.Point(m1.x + tx, m1.y + ty),
43693 c2: new scope.Point(m2.x + tx, m2.y + ty)
43696 Bezier.prototype.length = function () {
43701 for (var i = 0; i <= steps; i += 1) {
43703 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43704 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43706 var xdiff = cx - px;
43707 var ydiff = cy - py;
43708 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43715 Bezier.prototype.point = function (t, start, c1, c2, end) {
43716 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43717 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43718 + (3.0 * c2 * (1.0 - t) * t * t)
43719 + (end * t * t * t);
43724 throttleStroke: function(fn, wait) {
43725 if (wait === void 0) { wait = 250; }
43727 var timeout = null;
43731 var later = function () {
43732 previous = Date.now();
43734 result = fn.apply(storedContext, storedArgs);
43736 storedContext = null;
43740 return function wrapper() {
43742 for (var _i = 0; _i < arguments.length; _i++) {
43743 args[_i] = arguments[_i];
43745 var now = Date.now();
43746 var remaining = wait - (now - previous);
43747 storedContext = this;
43749 if (remaining <= 0 || remaining > wait) {
43751 clearTimeout(timeout);
43755 result = fn.apply(storedContext, storedArgs);
43757 storedContext = null;
43761 else if (!timeout) {
43762 timeout = window.setTimeout(later, remaining);