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|light|dark) default
965 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) 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);
998 * When a butotn is pressed
999 * @param {Roo.bootstrap.Button} btn
1000 * @param {Roo.EventObject} e
1005 * After the button has been toggles
1006 * @param {Roo.bootstrap.Button} btn
1007 * @param {Roo.EventObject} e
1008 * @param {boolean} pressed (also available as button.pressed)
1014 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1035 preventDefault: true,
1043 getAutoCreate : function(){
1051 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1052 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1053 this.tag = 'button';
1057 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1059 if (this.toggle == true) {
1062 cls: 'slider-frame roo-button',
1066 'data-on-text':'ON',
1067 'data-off-text':'OFF',
1068 cls: 'slider-button',
1074 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1075 cfg.cls += ' '+this.weight;
1082 cfg.cls += ' close';
1084 cfg["aria-hidden"] = true;
1086 cfg.html = "×";
1092 if (this.theme==='default') {
1093 cfg.cls = 'btn roo-button';
1095 //if (this.parentType != 'Navbar') {
1096 this.weight = this.weight.length ? this.weight : 'default';
1098 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1100 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1101 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1102 cfg.cls += ' btn-' + outline + weight;
1103 if (this.weight == 'default') {
1105 cfg.cls += ' btn-' + this.weight;
1108 } else if (this.theme==='glow') {
1111 cfg.cls = 'btn-glow roo-button';
1113 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1115 cfg.cls += ' ' + this.weight;
1121 this.cls += ' inverse';
1125 if (this.active || this.pressed === true) {
1126 cfg.cls += ' active';
1129 if (this.disabled) {
1130 cfg.disabled = 'disabled';
1134 Roo.log('changing to ul' );
1136 this.glyphicon = 'caret';
1137 if (Roo.bootstrap.version == 4) {
1138 this.fa = 'caret-down';
1143 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1145 //gsRoo.log(this.parentType);
1146 if (this.parentType === 'Navbar' && !this.parent().bar) {
1147 Roo.log('changing to li?');
1156 href : this.href || '#'
1159 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1160 cfg.cls += ' dropdown';
1167 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1169 if (this.glyphicon) {
1170 cfg.html = ' ' + cfg.html;
1175 cls: 'glyphicon glyphicon-' + this.glyphicon
1180 cfg.html = ' ' + cfg.html;
1185 cls: 'fa fas fa-' + this.fa
1195 // cfg.cls='btn roo-button';
1199 var value = cfg.html;
1204 cls: 'glyphicon glyphicon-' + this.glyphicon,
1211 cls: 'fa fas fa-' + this.fa,
1216 var bw = this.badge_weight.length ? this.badge_weight :
1217 (this.weight.length ? this.weight : 'secondary');
1218 bw = bw == 'default' ? 'secondary' : bw;
1224 cls: 'badge badge-' + bw,
1233 cfg.cls += ' dropdown';
1234 cfg.html = typeof(cfg.html) != 'undefined' ?
1235 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1238 if (cfg.tag !== 'a' && this.href !== '') {
1239 throw "Tag must be a to set href.";
1240 } else if (this.href.length > 0) {
1241 cfg.href = this.href;
1244 if(this.removeClass){
1249 cfg.target = this.target;
1254 initEvents: function() {
1255 // Roo.log('init events?');
1256 // Roo.log(this.el.dom);
1259 if (typeof (this.menu) != 'undefined') {
1260 this.menu.parentType = this.xtype;
1261 this.menu.triggerEl = this.el;
1262 this.addxtype(Roo.apply({}, this.menu));
1266 if (this.el.hasClass('roo-button')) {
1267 this.el.on('click', this.onClick, this);
1269 this.el.select('.roo-button').on('click', this.onClick, this);
1272 if(this.removeClass){
1273 this.el.on('click', this.onClick, this);
1276 this.el.enableDisplayMode();
1279 onClick : function(e)
1281 if (this.disabled) {
1285 Roo.log('button on click ');
1286 if(this.preventDefault){
1290 if (this.pressed === true || this.pressed === false) {
1291 this.toggleActive(e);
1295 this.fireEvent('click', this, e);
1299 * Enables this button
1303 this.disabled = false;
1304 this.el.removeClass('disabled');
1308 * Disable this button
1310 disable : function()
1312 this.disabled = true;
1313 this.el.addClass('disabled');
1316 * sets the active state on/off,
1317 * @param {Boolean} state (optional) Force a particular state
1319 setActive : function(v) {
1321 this.el[v ? 'addClass' : 'removeClass']('active');
1325 * toggles the current active state
1327 toggleActive : function(e)
1329 this.setActive(!this.pressed);
1330 this.fireEvent('toggle', this, e, !this.pressed);
1333 * get the current active state
1334 * @return {boolean} true if it's active
1336 isActive : function()
1338 return this.el.hasClass('active');
1341 * set the text of the first selected button
1343 setText : function(str)
1345 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1348 * get the text of the first selected button
1350 getText : function()
1352 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1355 setWeight : function(str)
1357 this.el.removeClass(Roo.bootstrap.Button.weightClass );
1359 var outline = this.outline ? 'outline-' : '';
1360 if (str == 'default') {
1361 this.el.addClass('btn-default btn-outline-secondary');
1364 this.el.addClass('btn-' + outline + str);
1369 Roo.bootstrap.Button.weightClass = [
1372 "btn-outline-secondary",
1390 * @class Roo.bootstrap.Column
1391 * @extends Roo.bootstrap.Component
1392 * Bootstrap Column class
1393 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1394 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1395 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1396 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1397 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1398 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1399 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1400 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1403 * @cfg {Boolean} hidden (true|false) hide the element
1404 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1405 * @cfg {String} fa (ban|check|...) font awesome icon
1406 * @cfg {Number} fasize (1|2|....) font awsome size
1408 * @cfg {String} icon (info-sign|check|...) glyphicon name
1410 * @cfg {String} html content of column.
1413 * Create a new Column
1414 * @param {Object} config The config object
1417 Roo.bootstrap.Column = function(config){
1418 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1421 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1439 getAutoCreate : function(){
1440 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1448 var sizes = ['xs','sm','md','lg'];
1449 sizes.map(function(size ,ix){
1450 //Roo.log( size + ':' + settings[size]);
1452 if (settings[size+'off'] !== false) {
1453 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1456 if (settings[size] === false) {
1460 if (!settings[size]) { // 0 = hidden
1461 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1463 for (var i = ix; i > -1; i--) {
1464 cfg.cls += ' d-' + sizes[i] + '-none';
1470 cfg.cls += ' col-' + size + '-' + settings[size] + (
1471 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1477 cfg.cls += ' hidden';
1480 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1481 cfg.cls +=' alert alert-' + this.alert;
1485 if (this.html.length) {
1486 cfg.html = this.html;
1490 if (this.fasize > 1) {
1491 fasize = ' fa-' + this.fasize + 'x';
1493 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1498 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1517 * @class Roo.bootstrap.Container
1518 * @extends Roo.bootstrap.Component
1519 * Bootstrap Container class
1520 * @cfg {Boolean} jumbotron is it a jumbotron element
1521 * @cfg {String} html content of element
1522 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1523 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1524 * @cfg {String} header content of header (for panel)
1525 * @cfg {String} footer content of footer (for panel)
1526 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1527 * @cfg {String} tag (header|aside|section) type of HTML tag.
1528 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1529 * @cfg {String} fa font awesome icon
1530 * @cfg {String} icon (info-sign|check|...) glyphicon name
1531 * @cfg {Boolean} hidden (true|false) hide the element
1532 * @cfg {Boolean} expandable (true|false) default false
1533 * @cfg {Boolean} expanded (true|false) default true
1534 * @cfg {String} rheader contet on the right of header
1535 * @cfg {Boolean} clickable (true|false) default false
1539 * Create a new Container
1540 * @param {Object} config The config object
1543 Roo.bootstrap.Container = function(config){
1544 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1550 * After the panel has been expand
1552 * @param {Roo.bootstrap.Container} this
1557 * After the panel has been collapsed
1559 * @param {Roo.bootstrap.Container} this
1564 * When a element is chick
1565 * @param {Roo.bootstrap.Container} this
1566 * @param {Roo.EventObject} e
1572 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1590 getChildContainer : function() {
1596 if (this.panel.length) {
1597 return this.el.select('.panel-body',true).first();
1604 getAutoCreate : function(){
1607 tag : this.tag || 'div',
1611 if (this.jumbotron) {
1612 cfg.cls = 'jumbotron';
1617 // - this is applied by the parent..
1619 // cfg.cls = this.cls + '';
1622 if (this.sticky.length) {
1624 var bd = Roo.get(document.body);
1625 if (!bd.hasClass('bootstrap-sticky')) {
1626 bd.addClass('bootstrap-sticky');
1627 Roo.select('html',true).setStyle('height', '100%');
1630 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1634 if (this.well.length) {
1635 switch (this.well) {
1638 cfg.cls +=' well well-' +this.well;
1647 cfg.cls += ' hidden';
1651 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1652 cfg.cls +=' alert alert-' + this.alert;
1657 if (this.panel.length) {
1658 cfg.cls += ' panel panel-' + this.panel;
1660 if (this.header.length) {
1664 if(this.expandable){
1666 cfg.cls = cfg.cls + ' expandable';
1670 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1678 cls : 'panel-title',
1679 html : (this.expandable ? ' ' : '') + this.header
1683 cls: 'panel-header-right',
1689 cls : 'panel-heading',
1690 style : this.expandable ? 'cursor: pointer' : '',
1698 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1703 if (this.footer.length) {
1705 cls : 'panel-footer',
1714 body.html = this.html || cfg.html;
1715 // prefix with the icons..
1717 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1720 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1725 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1726 cfg.cls = 'container';
1732 initEvents: function()
1734 if(this.expandable){
1735 var headerEl = this.headerEl();
1738 headerEl.on('click', this.onToggleClick, this);
1743 this.el.on('click', this.onClick, this);
1748 onToggleClick : function()
1750 var headerEl = this.headerEl();
1766 if(this.fireEvent('expand', this)) {
1768 this.expanded = true;
1770 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1772 this.el.select('.panel-body',true).first().removeClass('hide');
1774 var toggleEl = this.toggleEl();
1780 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1785 collapse : function()
1787 if(this.fireEvent('collapse', this)) {
1789 this.expanded = false;
1791 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1792 this.el.select('.panel-body',true).first().addClass('hide');
1794 var toggleEl = this.toggleEl();
1800 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1804 toggleEl : function()
1806 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1810 return this.el.select('.panel-heading .fa',true).first();
1813 headerEl : function()
1815 if(!this.el || !this.panel.length || !this.header.length){
1819 return this.el.select('.panel-heading',true).first()
1824 if(!this.el || !this.panel.length){
1828 return this.el.select('.panel-body',true).first()
1831 titleEl : function()
1833 if(!this.el || !this.panel.length || !this.header.length){
1837 return this.el.select('.panel-title',true).first();
1840 setTitle : function(v)
1842 var titleEl = this.titleEl();
1848 titleEl.dom.innerHTML = v;
1851 getTitle : function()
1854 var titleEl = this.titleEl();
1860 return titleEl.dom.innerHTML;
1863 setRightTitle : function(v)
1865 var t = this.el.select('.panel-header-right',true).first();
1871 t.dom.innerHTML = v;
1874 onClick : function(e)
1878 this.fireEvent('click', this, e);
1885 * This is BS4's Card element.. - similar to our containers probably..
1889 * @class Roo.bootstrap.Card
1890 * @extends Roo.bootstrap.Component
1891 * Bootstrap Card class
1894 * possible... may not be implemented..
1895 * @cfg {String} header_image src url of image.
1896 * @cfg {String|Object} header
1897 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1899 * @cfg {String} title
1900 * @cfg {String} subtitle
1901 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1902 * @cfg {String} footer
1904 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1906 * @cfg {String} margin (0|1|2|3|4|5|auto)
1907 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1908 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1909 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1910 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1911 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1912 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1914 * @cfg {String} padding (0|1|2|3|4|5)
1915 * @cfg {String} padding_top (0|1|2|3|4|5)
1916 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1917 * @cfg {String} padding_left (0|1|2|3|4|5)
1918 * @cfg {String} padding_right (0|1|2|3|4|5)
1919 * @cfg {String} padding_x (0|1|2|3|4|5)
1920 * @cfg {String} padding_y (0|1|2|3|4|5)
1922 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1923 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1924 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928 * @config {Boolean} dragable if this card can be dragged.
1929 * @config {String} drag_group group for drag
1930 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1931 * @config {String} drop_group group for drag
1933 * @config {Boolean} collapsable can the body be collapsed.
1934 * @config {Boolean} collapsed is the body collapsed when rendered...
1935 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1936 * @config {Boolean} rotated is the body rotated when rendered...
1939 * Create a new Container
1940 * @param {Object} config The config object
1943 Roo.bootstrap.Card = function(config){
1944 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1950 * When a element a card is dropped
1951 * @param {Roo.bootstrap.Element} this
1952 * @param {Roo.Element} n the node being dropped?
1953 * @param {Object} dd Drag and drop data
1954 * @param {Roo.EventObject} e
1955 * @param {Roo.EventObject} data the data passed via getDragData
1960 * When a element a card is rotate
1961 * @param {Roo.bootstrap.Element} this
1962 * @param {Roo.Element} n the node being dropped?
1963 * @param {Boolean} rotate status
1971 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1976 margin: '', /// may be better in component?
2006 collapsable : false,
2015 childContainer : false,
2016 dropEl : false, /// the dom placeholde element that indicates drop location.
2018 layoutCls : function()
2022 Roo.log(this.margin_bottom.length);
2023 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2024 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2026 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2027 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2029 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2030 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2034 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2035 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2036 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2040 // more generic support?
2048 // Roo.log("Call onRender: " + this.xtype);
2049 /* We are looking at something like this.
2051 <img src="..." class="card-img-top" alt="...">
2052 <div class="card-body">
2053 <h5 class="card-title">Card title</h5>
2054 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2056 >> this bit is really the body...
2057 <div> << we will ad dthis in hopefully it will not break shit.
2059 ** card text does not actually have any styling...
2061 <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>
2064 <a href="#" class="card-link">Card link</a>
2067 <div class="card-footer">
2068 <small class="text-muted">Last updated 3 mins ago</small>
2072 getAutoCreate : function(){
2080 if (this.weight.length && this.weight != 'light') {
2081 cfg.cls += ' text-white';
2083 cfg.cls += ' text-dark'; // need as it's nested..
2085 if (this.weight.length) {
2086 cfg.cls += ' bg-' + this.weight;
2089 cfg.cls += this.layoutCls();
2092 if (this.header.length) {
2094 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2095 cls : 'card-header',
2103 cls : 'card-header d-none',
2108 if (this.collapsable) {
2111 cls : 'd-block user-select-none',
2115 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2120 hdr.cn.push(hdr_ctr);
2125 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2130 if (this.header_image.length) {
2133 cls : 'card-img-top',
2134 src: this.header_image // escape?
2139 cls : 'card-img-top d-none'
2145 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2149 if (this.collapsable || this.rotateable) {
2152 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2159 if (this.title.length) {
2163 src: this.title // escape?
2167 if (this.subtitle.length) {
2171 src: this.subtitle // escape?
2177 cls : 'roo-card-body-ctr'
2180 if (this.html.length) {
2186 // fixme ? handle objects?
2188 if (this.footer.length) {
2191 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2196 cfg.cn.push({cls : 'card-footer d-none'});
2205 getCardHeader : function()
2207 var ret = this.el.select('.card-header',true).first();
2208 if (ret.hasClass('d-none')) {
2209 ret.removeClass('d-none');
2214 getCardFooter : function()
2216 var ret = this.el.select('.card-footer',true).first();
2217 if (ret.hasClass('d-none')) {
2218 ret.removeClass('d-none');
2223 getCardImageTop : function()
2225 var ret = this.el.select('.card-img-top',true).first();
2226 if (ret.hasClass('d-none')) {
2227 ret.removeClass('d-none');
2233 getChildContainer : function()
2239 return this.el.select('.roo-card-body-ctr',true).first();
2242 initEvents: function()
2245 this.bodyEl = this.getChildContainer();
2247 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2248 containerScroll: true,
2249 ddGroup: this.drag_group || 'default_card_drag_group'
2251 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2253 if (this.dropable) {
2254 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2255 containerScroll: true,
2256 ddGroup: this.drop_group || 'default_card_drag_group'
2258 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2259 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2260 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2261 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2262 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2265 if (this.collapsable) {
2266 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2268 if (this.rotateable) {
2269 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2271 this.collapsableEl = this.el.select('.roo-collapsable').first();
2273 this.footerEl = this.el.select('.card-footer').first();
2274 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2275 this.headerEl = this.el.select('.roo-card-header-ctr').first();
2278 this.el.addClass('roo-card-rotated');
2279 this.fireEvent('rotate', this, true);
2283 getDragData : function(e)
2285 var target = this.getEl();
2287 //this.handleSelection(e);
2292 nodes: this.getEl(),
2297 dragData.ddel = target.dom ; // the div element
2298 Roo.log(target.getWidth( ));
2299 dragData.ddel.style.width = target.getWidth() + 'px';
2306 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2307 * whole Element becomes the target, and this causes the drop gesture to append.
2309 getTargetFromEvent : function(e, dragged_card_el)
2311 var target = e.getTarget();
2312 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2313 target = target.parentNode;
2324 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2325 // see if target is one of the 'cards'...
2328 //Roo.log(this.items.length);
2331 var last_card_n = 0;
2333 for (var i = 0;i< this.items.length;i++) {
2335 if (!this.items[i].el.hasClass('card')) {
2338 pos = this.getDropPoint(e, this.items[i].el.dom);
2340 cards_len = ret.cards.length;
2341 //Roo.log(this.items[i].el.dom.id);
2342 ret.cards.push(this.items[i]);
2344 if (ret.card_n < 0 && pos == 'above') {
2345 ret.position = cards_len > 0 ? 'below' : pos;
2346 ret.items_n = i > 0 ? i - 1 : 0;
2347 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2348 ret.card = ret.cards[ret.card_n];
2351 if (!ret.cards.length) {
2353 ret.position = 'below';
2357 // could not find a card.. stick it at the end..
2358 if (ret.card_n < 0) {
2359 ret.card_n = last_card_n;
2360 ret.card = ret.cards[last_card_n];
2361 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2362 ret.position = 'below';
2365 if (this.items[ret.items_n].el == dragged_card_el) {
2369 if (ret.position == 'below') {
2370 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2372 if (card_after && card_after.el == dragged_card_el) {
2379 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2381 if (card_before && card_before.el == dragged_card_el) {
2388 onNodeEnter : function(n, dd, e, data){
2391 onNodeOver : function(n, dd, e, data)
2394 var target_info = this.getTargetFromEvent(e,data.source.el);
2395 if (target_info === false) {
2396 this.dropPlaceHolder('hide');
2399 Roo.log(['getTargetFromEvent', target_info ]);
2402 this.dropPlaceHolder('show', target_info,data);
2406 onNodeOut : function(n, dd, e, data){
2407 this.dropPlaceHolder('hide');
2410 onNodeDrop : function(n, dd, e, data)
2413 // call drop - return false if
2415 // this could actually fail - if the Network drops..
2416 // we will ignore this at present..- client should probably reload
2417 // the whole set of cards if stuff like that fails.
2420 var info = this.getTargetFromEvent(e,data.source.el);
2421 if (info === false) {
2425 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2429 this.dropPlaceHolder('hide');
2431 // do the dom manipulation first..
2432 var dom = data.source.el.dom;
2433 dom.parentNode.removeChild(dom);
2436 if (info.card !== true) {
2437 var cardel = info.card.el.dom;
2439 if (info.position == 'above') {
2440 cardel.parentNode.insertBefore(dom, cardel);
2441 } else if (cardel.nextSibling) {
2442 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2444 cardel.parentNode.append(dom);
2447 // card container???
2448 this.bodyEl.dom.append(dom);
2451 //FIXME HANDLE card = true
2453 // add this to the correct place in items.
2457 // remove Card from items.
2459 var old_parent = data.source.parent();
2461 old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2463 if (this.items.length) {
2465 //Roo.log([info.items_n, info.position, this.items.length]);
2466 for (var i =0; i < this.items.length; i++) {
2467 if (i == info.items_n && info.position == 'above') {
2468 nitems.push(data.source);
2470 nitems.push(this.items[i]);
2471 if (i == info.items_n && info.position == 'below') {
2472 nitems.push(data.source);
2475 this.items = nitems;
2476 Roo.log(this.items);
2478 this.items.push(data.source);
2481 data.source.parentId = this.id;
2486 /** Decide whether to drop above or below a View node. */
2487 getDropPoint : function(e, n, dd)
2492 if (n == this.bodyEl.dom) {
2495 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2496 var c = t + (b - t) / 2;
2497 var y = Roo.lib.Event.getPageY(e);
2504 onToggleCollapse : function(e)
2506 if (this.collapsed) {
2507 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2508 this.collapsableEl.addClass('show');
2509 this.collapsed = false;
2512 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2513 this.collapsableEl.removeClass('show');
2514 this.collapsed = true;
2519 onToggleRotate : function(e)
2521 this.collapsableEl.removeClass('show');
2522 this.footerEl.removeClass('d-none');
2523 this.el.removeClass('roo-card-rotated');
2524 this.el.removeClass('d-none');
2527 this.collapsableEl.addClass('show');
2528 this.rotated = false;
2529 this.fireEvent('rotate', this, this.rotated);
2532 this.el.addClass('roo-card-rotated');
2533 this.footerEl.addClass('d-none');
2534 this.el.select('.roo-collapsable').removeClass('show');
2536 this.rotated = true;
2537 this.fireEvent('rotate', this, this.rotated);
2541 dropPlaceHolder: function (action, info, data)
2543 if (this.dropEl === false) {
2544 this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2548 this.dropEl.removeClass(['d-none', 'd-block']);
2549 if (action == 'hide') {
2551 this.dropEl.addClass('d-none');
2554 // FIXME - info.card == true!!!
2555 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2557 if (info.card !== true) {
2558 var cardel = info.card.el.dom;
2560 if (info.position == 'above') {
2561 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2562 } else if (cardel.nextSibling) {
2563 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2565 cardel.parentNode.append(this.dropEl.dom);
2568 // card container???
2569 this.bodyEl.dom.append(this.dropEl.dom);
2572 this.dropEl.addClass('d-block roo-card-dropzone');
2574 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2581 setHeaderText: function(html)
2583 this.headerEl.dom.innerHTML = html;
2592 * Card header - holder for the card header elements.
2597 * @class Roo.bootstrap.CardHeader
2598 * @extends Roo.bootstrap.Element
2599 * Bootstrap CardHeader class
2601 * Create a new Card Header - that you can embed children into
2602 * @param {Object} config The config object
2605 Roo.bootstrap.CardHeader = function(config){
2606 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2609 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2612 container_method : 'getCardHeader'
2625 * Card footer - holder for the card footer elements.
2630 * @class Roo.bootstrap.CardFooter
2631 * @extends Roo.bootstrap.Element
2632 * Bootstrap CardFooter class
2634 * Create a new Card Footer - that you can embed children into
2635 * @param {Object} config The config object
2638 Roo.bootstrap.CardFooter = function(config){
2639 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2642 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2645 container_method : 'getCardFooter'
2658 * Card header - holder for the card header elements.
2663 * @class Roo.bootstrap.CardImageTop
2664 * @extends Roo.bootstrap.Element
2665 * Bootstrap CardImageTop class
2667 * Create a new Card Image Top container
2668 * @param {Object} config The config object
2671 Roo.bootstrap.CardImageTop = function(config){
2672 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2675 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2678 container_method : 'getCardImageTop'
2696 * @class Roo.bootstrap.Img
2697 * @extends Roo.bootstrap.Component
2698 * Bootstrap Img class
2699 * @cfg {Boolean} imgResponsive false | true
2700 * @cfg {String} border rounded | circle | thumbnail
2701 * @cfg {String} src image source
2702 * @cfg {String} alt image alternative text
2703 * @cfg {String} href a tag href
2704 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2705 * @cfg {String} xsUrl xs image source
2706 * @cfg {String} smUrl sm image source
2707 * @cfg {String} mdUrl md image source
2708 * @cfg {String} lgUrl lg image source
2711 * Create a new Input
2712 * @param {Object} config The config object
2715 Roo.bootstrap.Img = function(config){
2716 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2722 * The img click event for the img.
2723 * @param {Roo.EventObject} e
2729 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2731 imgResponsive: true,
2741 getAutoCreate : function()
2743 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2744 return this.createSingleImg();
2749 cls: 'roo-image-responsive-group',
2754 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2756 if(!_this[size + 'Url']){
2762 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2763 html: _this.html || cfg.html,
2764 src: _this[size + 'Url']
2767 img.cls += ' roo-image-responsive-' + size;
2769 var s = ['xs', 'sm', 'md', 'lg'];
2771 s.splice(s.indexOf(size), 1);
2773 Roo.each(s, function(ss){
2774 img.cls += ' hidden-' + ss;
2777 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2778 cfg.cls += ' img-' + _this.border;
2782 cfg.alt = _this.alt;
2795 a.target = _this.target;
2799 cfg.cn.push((_this.href) ? a : img);
2806 createSingleImg : function()
2810 cls: (this.imgResponsive) ? 'img-responsive' : '',
2812 src : 'about:blank' // just incase src get's set to undefined?!?
2815 cfg.html = this.html || cfg.html;
2817 cfg.src = this.src || cfg.src;
2819 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2820 cfg.cls += ' img-' + this.border;
2837 a.target = this.target;
2842 return (this.href) ? a : cfg;
2845 initEvents: function()
2848 this.el.on('click', this.onClick, this);
2853 onClick : function(e)
2855 Roo.log('img onclick');
2856 this.fireEvent('click', this, e);
2859 * Sets the url of the image - used to update it
2860 * @param {String} url the url of the image
2863 setSrc : function(url)
2867 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2868 this.el.dom.src = url;
2872 this.el.select('img', true).first().dom.src = url;
2888 * @class Roo.bootstrap.Link
2889 * @extends Roo.bootstrap.Component
2890 * Bootstrap Link Class
2891 * @cfg {String} alt image alternative text
2892 * @cfg {String} href a tag href
2893 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2894 * @cfg {String} html the content of the link.
2895 * @cfg {String} anchor name for the anchor link
2896 * @cfg {String} fa - favicon
2898 * @cfg {Boolean} preventDefault (true | false) default false
2902 * Create a new Input
2903 * @param {Object} config The config object
2906 Roo.bootstrap.Link = function(config){
2907 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2913 * The img click event for the img.
2914 * @param {Roo.EventObject} e
2920 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2924 preventDefault: false,
2930 getAutoCreate : function()
2932 var html = this.html || '';
2934 if (this.fa !== false) {
2935 html = '<i class="fa fa-' + this.fa + '"></i>';
2940 // anchor's do not require html/href...
2941 if (this.anchor === false) {
2943 cfg.href = this.href || '#';
2945 cfg.name = this.anchor;
2946 if (this.html !== false || this.fa !== false) {
2949 if (this.href !== false) {
2950 cfg.href = this.href;
2954 if(this.alt !== false){
2959 if(this.target !== false) {
2960 cfg.target = this.target;
2966 initEvents: function() {
2968 if(!this.href || this.preventDefault){
2969 this.el.on('click', this.onClick, this);
2973 onClick : function(e)
2975 if(this.preventDefault){
2978 //Roo.log('img onclick');
2979 this.fireEvent('click', this, e);
2992 * @class Roo.bootstrap.Header
2993 * @extends Roo.bootstrap.Component
2994 * Bootstrap Header class
2995 * @cfg {String} html content of header
2996 * @cfg {Number} level (1|2|3|4|5|6) default 1
2999 * Create a new Header
3000 * @param {Object} config The config object
3004 Roo.bootstrap.Header = function(config){
3005 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3008 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3016 getAutoCreate : function(){
3021 tag: 'h' + (1 *this.level),
3022 html: this.html || ''
3034 * Ext JS Library 1.1.1
3035 * Copyright(c) 2006-2007, Ext JS, LLC.
3037 * Originally Released Under LGPL - original licence link has changed is not relivant.
3040 * <script type="text/javascript">
3044 * @class Roo.bootstrap.MenuMgr
3045 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3048 Roo.bootstrap.MenuMgr = function(){
3049 var menus, active, groups = {}, attached = false, lastShow = new Date();
3051 // private - called when first menu is created
3054 active = new Roo.util.MixedCollection();
3055 Roo.get(document).addKeyListener(27, function(){
3056 if(active.length > 0){
3064 if(active && active.length > 0){
3065 var c = active.clone();
3075 if(active.length < 1){
3076 Roo.get(document).un("mouseup", onMouseDown);
3084 var last = active.last();
3085 lastShow = new Date();
3088 Roo.get(document).on("mouseup", onMouseDown);
3093 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3094 m.parentMenu.activeChild = m;
3095 }else if(last && last.isVisible()){
3096 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3101 function onBeforeHide(m){
3103 m.activeChild.hide();
3105 if(m.autoHideTimer){
3106 clearTimeout(m.autoHideTimer);
3107 delete m.autoHideTimer;
3112 function onBeforeShow(m){
3113 var pm = m.parentMenu;
3114 if(!pm && !m.allowOtherMenus){
3116 }else if(pm && pm.activeChild && active != m){
3117 pm.activeChild.hide();
3121 // private this should really trigger on mouseup..
3122 function onMouseDown(e){
3123 Roo.log("on Mouse Up");
3125 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3126 Roo.log("MenuManager hideAll");
3135 function onBeforeCheck(mi, state){
3137 var g = groups[mi.group];
3138 for(var i = 0, l = g.length; i < l; i++){
3140 g[i].setChecked(false);
3149 * Hides all menus that are currently visible
3151 hideAll : function(){
3156 register : function(menu){
3160 menus[menu.id] = menu;
3161 menu.on("beforehide", onBeforeHide);
3162 menu.on("hide", onHide);
3163 menu.on("beforeshow", onBeforeShow);
3164 menu.on("show", onShow);
3166 if(g && menu.events["checkchange"]){
3170 groups[g].push(menu);
3171 menu.on("checkchange", onCheck);
3176 * Returns a {@link Roo.menu.Menu} object
3177 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3178 * be used to generate and return a new Menu instance.
3180 get : function(menu){
3181 if(typeof menu == "string"){ // menu id
3183 }else if(menu.events){ // menu instance
3186 /*else if(typeof menu.length == 'number'){ // array of menu items?
3187 return new Roo.bootstrap.Menu({items:menu});
3188 }else{ // otherwise, must be a config
3189 return new Roo.bootstrap.Menu(menu);
3196 unregister : function(menu){
3197 delete menus[menu.id];
3198 menu.un("beforehide", onBeforeHide);
3199 menu.un("hide", onHide);
3200 menu.un("beforeshow", onBeforeShow);
3201 menu.un("show", onShow);
3203 if(g && menu.events["checkchange"]){
3204 groups[g].remove(menu);
3205 menu.un("checkchange", onCheck);
3210 registerCheckable : function(menuItem){
3211 var g = menuItem.group;
3216 groups[g].push(menuItem);
3217 menuItem.on("beforecheckchange", onBeforeCheck);
3222 unregisterCheckable : function(menuItem){
3223 var g = menuItem.group;
3225 groups[g].remove(menuItem);
3226 menuItem.un("beforecheckchange", onBeforeCheck);
3238 * @class Roo.bootstrap.Menu
3239 * @extends Roo.bootstrap.Component
3240 * Bootstrap Menu class - container for MenuItems
3241 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3242 * @cfg {bool} hidden if the menu should be hidden when rendered.
3243 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3244 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3248 * @param {Object} config The config object
3252 Roo.bootstrap.Menu = function(config){
3253 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3254 if (this.registerMenu && this.type != 'treeview') {
3255 Roo.bootstrap.MenuMgr.register(this);
3262 * Fires before this menu is displayed (return false to block)
3263 * @param {Roo.menu.Menu} this
3268 * Fires before this menu is hidden (return false to block)
3269 * @param {Roo.menu.Menu} this
3274 * Fires after this menu is displayed
3275 * @param {Roo.menu.Menu} this
3280 * Fires after this menu is hidden
3281 * @param {Roo.menu.Menu} this
3286 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3287 * @param {Roo.menu.Menu} this
3288 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3289 * @param {Roo.EventObject} e
3294 * Fires when the mouse is hovering over this menu
3295 * @param {Roo.menu.Menu} this
3296 * @param {Roo.EventObject} e
3297 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3302 * Fires when the mouse exits this menu
3303 * @param {Roo.menu.Menu} this
3304 * @param {Roo.EventObject} e
3305 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3310 * Fires when a menu item contained in this menu is clicked
3311 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3312 * @param {Roo.EventObject} e
3316 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3319 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3323 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3326 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3328 registerMenu : true,
3330 menuItems :false, // stores the menu items..
3340 getChildContainer : function() {
3344 getAutoCreate : function(){
3346 //if (['right'].indexOf(this.align)!==-1) {
3347 // cfg.cn[1].cls += ' pull-right'
3353 cls : 'dropdown-menu' ,
3354 style : 'z-index:1000'
3358 if (this.type === 'submenu') {
3359 cfg.cls = 'submenu active';
3361 if (this.type === 'treeview') {
3362 cfg.cls = 'treeview-menu';
3367 initEvents : function() {
3369 // Roo.log("ADD event");
3370 // Roo.log(this.triggerEl.dom);
3372 this.triggerEl.on('click', this.onTriggerClick, this);
3374 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3377 if (this.triggerEl.hasClass('nav-item')) {
3378 // dropdown toggle on the 'a' in BS4?
3379 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3381 this.triggerEl.addClass('dropdown-toggle');
3384 this.el.on('touchstart' , this.onTouch, this);
3386 this.el.on('click' , this.onClick, this);
3388 this.el.on("mouseover", this.onMouseOver, this);
3389 this.el.on("mouseout", this.onMouseOut, this);
3393 findTargetItem : function(e)
3395 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3399 //Roo.log(t); Roo.log(t.id);
3401 //Roo.log(this.menuitems);
3402 return this.menuitems.get(t.id);
3404 //return this.items.get(t.menuItemId);
3410 onTouch : function(e)
3412 Roo.log("menu.onTouch");
3413 //e.stopEvent(); this make the user popdown broken
3417 onClick : function(e)
3419 Roo.log("menu.onClick");
3421 var t = this.findTargetItem(e);
3422 if(!t || t.isContainer){
3427 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3428 if(t == this.activeItem && t.shouldDeactivate(e)){
3429 this.activeItem.deactivate();
3430 delete this.activeItem;
3434 this.setActiveItem(t, true);
3442 Roo.log('pass click event');
3446 this.fireEvent("click", this, t, e);
3450 if(!t.href.length || t.href == '#'){
3451 (function() { _this.hide(); }).defer(100);
3456 onMouseOver : function(e){
3457 var t = this.findTargetItem(e);
3460 // if(t.canActivate && !t.disabled){
3461 // this.setActiveItem(t, true);
3465 this.fireEvent("mouseover", this, e, t);
3467 isVisible : function(){
3468 return !this.hidden;
3470 onMouseOut : function(e){
3471 var t = this.findTargetItem(e);
3474 // if(t == this.activeItem && t.shouldDeactivate(e)){
3475 // this.activeItem.deactivate();
3476 // delete this.activeItem;
3479 this.fireEvent("mouseout", this, e, t);
3484 * Displays this menu relative to another element
3485 * @param {String/HTMLElement/Roo.Element} element The element to align to
3486 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3487 * the element (defaults to this.defaultAlign)
3488 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3490 show : function(el, pos, parentMenu)
3492 if (false === this.fireEvent("beforeshow", this)) {
3493 Roo.log("show canceled");
3496 this.parentMenu = parentMenu;
3501 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3504 * Displays this menu at a specific xy position
3505 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3506 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3508 showAt : function(xy, parentMenu, /* private: */_e){
3509 this.parentMenu = parentMenu;
3514 this.fireEvent("beforeshow", this);
3515 //xy = this.el.adjustForConstraints(xy);
3519 this.hideMenuItems();
3520 this.hidden = false;
3521 this.triggerEl.addClass('open');
3522 this.el.addClass('show');
3524 // reassign x when hitting right
3525 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3526 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3529 // reassign y when hitting bottom
3530 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3531 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3534 // but the list may align on trigger left or trigger top... should it be a properity?
3536 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3541 this.fireEvent("show", this);
3547 this.doFocus.defer(50, this);
3551 doFocus : function(){
3553 this.focusEl.focus();
3558 * Hides this menu and optionally all parent menus
3559 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3561 hide : function(deep)
3563 if (false === this.fireEvent("beforehide", this)) {
3564 Roo.log("hide canceled");
3567 this.hideMenuItems();
3568 if(this.el && this.isVisible()){
3570 if(this.activeItem){
3571 this.activeItem.deactivate();
3572 this.activeItem = null;
3574 this.triggerEl.removeClass('open');;
3575 this.el.removeClass('show');
3577 this.fireEvent("hide", this);
3579 if(deep === true && this.parentMenu){
3580 this.parentMenu.hide(true);
3584 onTriggerClick : function(e)
3586 Roo.log('trigger click');
3588 var target = e.getTarget();
3590 Roo.log(target.nodeName.toLowerCase());
3592 if(target.nodeName.toLowerCase() === 'i'){
3598 onTriggerPress : function(e)
3600 Roo.log('trigger press');
3601 //Roo.log(e.getTarget());
3602 // Roo.log(this.triggerEl.dom);
3604 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3605 var pel = Roo.get(e.getTarget());
3606 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3607 Roo.log('is treeview or dropdown?');
3611 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3615 if (this.isVisible()) {
3620 this.show(this.triggerEl, '?', false);
3623 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3630 hideMenuItems : function()
3632 Roo.log("hide Menu Items");
3637 this.el.select('.open',true).each(function(aa) {
3639 aa.removeClass('open');
3643 addxtypeChild : function (tree, cntr) {
3644 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3646 this.menuitems.add(comp);
3658 this.getEl().dom.innerHTML = '';
3659 this.menuitems.clear();
3673 * @class Roo.bootstrap.MenuItem
3674 * @extends Roo.bootstrap.Component
3675 * Bootstrap MenuItem class
3676 * @cfg {String} html the menu label
3677 * @cfg {String} href the link
3678 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3679 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3680 * @cfg {Boolean} active used on sidebars to highlight active itesm
3681 * @cfg {String} fa favicon to show on left of menu item.
3682 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3686 * Create a new MenuItem
3687 * @param {Object} config The config object
3691 Roo.bootstrap.MenuItem = function(config){
3692 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3697 * The raw click event for the entire grid.
3698 * @param {Roo.bootstrap.MenuItem} this
3699 * @param {Roo.EventObject} e
3705 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3709 preventDefault: false,
3710 isContainer : false,
3714 getAutoCreate : function(){
3716 if(this.isContainer){
3719 cls: 'dropdown-menu-item '
3729 cls : 'dropdown-item',
3734 if (this.fa !== false) {
3737 cls : 'fa fa-' + this.fa
3746 cls: 'dropdown-menu-item',
3749 if (this.parent().type == 'treeview') {
3750 cfg.cls = 'treeview-menu';
3753 cfg.cls += ' active';
3758 anc.href = this.href || cfg.cn[0].href ;
3759 ctag.html = this.html || cfg.cn[0].html ;
3763 initEvents: function()
3765 if (this.parent().type == 'treeview') {
3766 this.el.select('a').on('click', this.onClick, this);
3770 this.menu.parentType = this.xtype;
3771 this.menu.triggerEl = this.el;
3772 this.menu = this.addxtype(Roo.apply({}, this.menu));
3776 onClick : function(e)
3778 Roo.log('item on click ');
3780 if(this.preventDefault){
3783 //this.parent().hideMenuItems();
3785 this.fireEvent('click', this, e);
3804 * @class Roo.bootstrap.MenuSeparator
3805 * @extends Roo.bootstrap.Component
3806 * Bootstrap MenuSeparator class
3809 * Create a new MenuItem
3810 * @param {Object} config The config object
3814 Roo.bootstrap.MenuSeparator = function(config){
3815 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3818 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3820 getAutoCreate : function(){
3839 * @class Roo.bootstrap.Modal
3840 * @extends Roo.bootstrap.Component
3841 * Bootstrap Modal class
3842 * @cfg {String} title Title of dialog
3843 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3844 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3845 * @cfg {Boolean} specificTitle default false
3846 * @cfg {Array} buttons Array of buttons or standard button set..
3847 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3848 * @cfg {Boolean} animate default true
3849 * @cfg {Boolean} allow_close default true
3850 * @cfg {Boolean} fitwindow default false
3851 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3852 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3853 * @cfg {String} size (sm|lg|xl) default empty
3854 * @cfg {Number} max_width set the max width of modal
3855 * @cfg {Boolean} editableTitle can the title be edited
3860 * Create a new Modal Dialog
3861 * @param {Object} config The config object
3864 Roo.bootstrap.Modal = function(config){
3865 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3870 * The raw btnclick event for the button
3871 * @param {Roo.EventObject} e
3876 * Fire when dialog resize
3877 * @param {Roo.bootstrap.Modal} this
3878 * @param {Roo.EventObject} e
3882 * @event titlechanged
3883 * Fire when the editable title has been changed
3884 * @param {Roo.bootstrap.Modal} this
3885 * @param {Roo.EventObject} value
3887 "titlechanged" : true
3890 this.buttons = this.buttons || [];
3893 this.tmpl = Roo.factory(this.tmpl);
3898 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3900 title : 'test dialog',
3910 specificTitle: false,
3912 buttonPosition: 'right',
3934 editableTitle : false,
3936 onRender : function(ct, position)
3938 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3941 var cfg = Roo.apply({}, this.getAutoCreate());
3944 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3946 //if (!cfg.name.length) {
3950 cfg.cls += ' ' + this.cls;
3953 cfg.style = this.style;
3955 this.el = Roo.get(document.body).createChild(cfg, position);
3957 //var type = this.el.dom.type;
3960 if(this.tabIndex !== undefined){
3961 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3964 this.dialogEl = this.el.select('.modal-dialog',true).first();
3965 this.bodyEl = this.el.select('.modal-body',true).first();
3966 this.closeEl = this.el.select('.modal-header .close', true).first();
3967 this.headerEl = this.el.select('.modal-header',true).first();
3968 this.titleEl = this.el.select('.modal-title',true).first();
3969 this.footerEl = this.el.select('.modal-footer',true).first();
3971 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3973 //this.el.addClass("x-dlg-modal");
3975 if (this.buttons.length) {
3976 Roo.each(this.buttons, function(bb) {
3977 var b = Roo.apply({}, bb);
3978 b.xns = b.xns || Roo.bootstrap;
3979 b.xtype = b.xtype || 'Button';
3980 if (typeof(b.listeners) == 'undefined') {
3981 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3984 var btn = Roo.factory(b);
3986 btn.render(this.getButtonContainer());
3990 // render the children.
3993 if(typeof(this.items) != 'undefined'){
3994 var items = this.items;
3997 for(var i =0;i < items.length;i++) {
3998 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4002 this.items = nitems;
4004 // where are these used - they used to be body/close/footer
4008 //this.el.addClass([this.fieldClass, this.cls]);
4012 getAutoCreate : function()
4014 // we will default to modal-body-overflow - might need to remove or make optional later.
4016 cls : 'modal-body enable-modal-body-overflow ',
4017 html : this.html || ''
4022 cls : 'modal-title',
4026 if(this.specificTitle){ // WTF is this?
4031 if (this.allow_close && Roo.bootstrap.version == 3) {
4041 if (this.editableTitle) {
4043 cls: 'form-control roo-editable-title d-none',
4049 if (this.allow_close && Roo.bootstrap.version == 4) {
4059 if(this.size.length){
4060 size = 'modal-' + this.size;
4063 var footer = Roo.bootstrap.version == 3 ?
4065 cls : 'modal-footer',
4069 cls: 'btn-' + this.buttonPosition
4074 { // BS4 uses mr-auto on left buttons....
4075 cls : 'modal-footer'
4086 cls: "modal-dialog " + size,
4089 cls : "modal-content",
4092 cls : 'modal-header',
4107 modal.cls += ' fade';
4113 getChildContainer : function() {
4118 getButtonContainer : function() {
4120 return Roo.bootstrap.version == 4 ?
4121 this.el.select('.modal-footer',true).first()
4122 : this.el.select('.modal-footer div',true).first();
4125 initEvents : function()
4127 if (this.allow_close) {
4128 this.closeEl.on('click', this.hide, this);
4130 Roo.EventManager.onWindowResize(this.resize, this, true);
4131 if (this.editableTitle) {
4132 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4133 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4134 this.headerEditEl.on('keyup', function(e) {
4135 if(e.isNavKeyPress()){
4136 this.toggleHeaderInput(false)
4139 this.headerEditEl.on('blur', function(e) {
4140 this.toggleHeaderInput(false)
4149 this.maskEl.setSize(
4150 Roo.lib.Dom.getViewWidth(true),
4151 Roo.lib.Dom.getViewHeight(true)
4154 if (this.fitwindow) {
4158 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4159 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4164 if(this.max_width !== 0) {
4166 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4169 this.setSize(w, this.height);
4173 if(this.max_height) {
4174 this.setSize(w,Math.min(
4176 Roo.lib.Dom.getViewportHeight(true) - 60
4182 if(!this.fit_content) {
4183 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4187 this.setSize(w, Math.min(
4189 this.headerEl.getHeight() +
4190 this.footerEl.getHeight() +
4191 this.getChildHeight(this.bodyEl.dom.childNodes),
4192 Roo.lib.Dom.getViewportHeight(true) - 60)
4198 setSize : function(w,h)
4209 if (!this.rendered) {
4213 //this.el.setStyle('display', 'block');
4214 this.el.removeClass('hideing');
4215 this.el.dom.style.display='block';
4217 Roo.get(document.body).addClass('modal-open');
4219 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4222 this.el.addClass('show');
4223 this.el.addClass('in');
4226 this.el.addClass('show');
4227 this.el.addClass('in');
4230 // not sure how we can show data in here..
4232 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4235 Roo.get(document.body).addClass("x-body-masked");
4237 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4238 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4239 this.maskEl.dom.style.display = 'block';
4240 this.maskEl.addClass('show');
4245 this.fireEvent('show', this);
4247 // set zindex here - otherwise it appears to be ignored...
4248 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4251 this.items.forEach( function(e) {
4252 e.layout ? e.layout() : false;
4260 if(this.fireEvent("beforehide", this) !== false){
4262 this.maskEl.removeClass('show');
4264 this.maskEl.dom.style.display = '';
4265 Roo.get(document.body).removeClass("x-body-masked");
4266 this.el.removeClass('in');
4267 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4269 if(this.animate){ // why
4270 this.el.addClass('hideing');
4271 this.el.removeClass('show');
4273 if (!this.el.hasClass('hideing')) {
4274 return; // it's been shown again...
4277 this.el.dom.style.display='';
4279 Roo.get(document.body).removeClass('modal-open');
4280 this.el.removeClass('hideing');
4284 this.el.removeClass('show');
4285 this.el.dom.style.display='';
4286 Roo.get(document.body).removeClass('modal-open');
4289 this.fireEvent('hide', this);
4292 isVisible : function()
4295 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4299 addButton : function(str, cb)
4303 var b = Roo.apply({}, { html : str } );
4304 b.xns = b.xns || Roo.bootstrap;
4305 b.xtype = b.xtype || 'Button';
4306 if (typeof(b.listeners) == 'undefined') {
4307 b.listeners = { click : cb.createDelegate(this) };
4310 var btn = Roo.factory(b);
4312 btn.render(this.getButtonContainer());
4318 setDefaultButton : function(btn)
4320 //this.el.select('.modal-footer').()
4323 resizeTo: function(w,h)
4325 this.dialogEl.setWidth(w);
4327 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4329 this.bodyEl.setHeight(h - diff);
4331 this.fireEvent('resize', this);
4334 setContentSize : function(w, h)
4338 onButtonClick: function(btn,e)
4341 this.fireEvent('btnclick', btn.name, e);
4344 * Set the title of the Dialog
4345 * @param {String} str new Title
4347 setTitle: function(str) {
4348 this.titleEl.dom.innerHTML = str;
4352 * Set the body of the Dialog
4353 * @param {String} str new Title
4355 setBody: function(str) {
4356 this.bodyEl.dom.innerHTML = str;
4359 * Set the body of the Dialog using the template
4360 * @param {Obj} data - apply this data to the template and replace the body contents.
4362 applyBody: function(obj)
4365 Roo.log("Error - using apply Body without a template");
4368 this.tmpl.overwrite(this.bodyEl, obj);
4371 getChildHeight : function(child_nodes)
4375 child_nodes.length == 0
4380 var child_height = 0;
4382 for(var i = 0; i < child_nodes.length; i++) {
4385 * for modal with tabs...
4386 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4388 var layout_childs = child_nodes[i].childNodes;
4390 for(var j = 0; j < layout_childs.length; j++) {
4392 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4394 var layout_body_childs = layout_childs[j].childNodes;
4396 for(var k = 0; k < layout_body_childs.length; k++) {
4398 if(layout_body_childs[k].classList.contains('navbar')) {
4399 child_height += layout_body_childs[k].offsetHeight;
4403 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4405 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4407 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4409 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4410 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4425 child_height += child_nodes[i].offsetHeight;
4426 // Roo.log(child_nodes[i].offsetHeight);
4429 return child_height;
4431 toggleHeaderInput : function(is_edit)
4434 if (is_edit && this.is_header_editing) {
4435 return; // already editing..
4439 this.headerEditEl.dom.value = this.title;
4440 this.headerEditEl.removeClass('d-none');
4441 this.headerEditEl.dom.focus();
4442 this.titleEl.addClass('d-none');
4444 this.is_header_editing = true;
4447 // flip back to not editing.
4448 this.title = this.headerEditEl.dom.value;
4449 this.headerEditEl.addClass('d-none');
4450 this.titleEl.removeClass('d-none');
4451 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4452 this.is_header_editing = false;
4453 this.fireEvent('titlechanged', this, this.title);
4462 Roo.apply(Roo.bootstrap.Modal, {
4464 * Button config that displays a single OK button
4473 * Button config that displays Yes and No buttons
4489 * Button config that displays OK and Cancel buttons
4504 * Button config that displays Yes, No and Cancel buttons
4529 * messagebox - can be used as a replace
4533 * @class Roo.MessageBox
4534 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4538 Roo.Msg.alert('Status', 'Changes saved successfully.');
4540 // Prompt for user data:
4541 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4543 // process text value...
4547 // Show a dialog using config options:
4549 title:'Save Changes?',
4550 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4551 buttons: Roo.Msg.YESNOCANCEL,
4558 Roo.bootstrap.MessageBox = function(){
4559 var dlg, opt, mask, waitTimer;
4560 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4561 var buttons, activeTextEl, bwidth;
4565 var handleButton = function(button){
4567 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4571 var handleHide = function(){
4573 dlg.el.removeClass(opt.cls);
4576 // Roo.TaskMgr.stop(waitTimer);
4577 // waitTimer = null;
4582 var updateButtons = function(b){
4585 buttons["ok"].hide();
4586 buttons["cancel"].hide();
4587 buttons["yes"].hide();
4588 buttons["no"].hide();
4589 dlg.footerEl.hide();
4593 dlg.footerEl.show();
4594 for(var k in buttons){
4595 if(typeof buttons[k] != "function"){
4598 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4599 width += buttons[k].el.getWidth()+15;
4609 var handleEsc = function(d, k, e){
4610 if(opt && opt.closable !== false){
4620 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4621 * @return {Roo.BasicDialog} The BasicDialog element
4623 getDialog : function(){
4625 dlg = new Roo.bootstrap.Modal( {
4628 //constraintoviewport:false,
4630 //collapsible : false,
4635 //buttonAlign:"center",
4636 closeClick : function(){
4637 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4640 handleButton("cancel");
4645 dlg.on("hide", handleHide);
4647 //dlg.addKeyListener(27, handleEsc);
4649 this.buttons = buttons;
4650 var bt = this.buttonText;
4651 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4652 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4653 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4654 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4656 bodyEl = dlg.bodyEl.createChild({
4658 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4659 '<textarea class="roo-mb-textarea"></textarea>' +
4660 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4662 msgEl = bodyEl.dom.firstChild;
4663 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4664 textboxEl.enableDisplayMode();
4665 textboxEl.addKeyListener([10,13], function(){
4666 if(dlg.isVisible() && opt && opt.buttons){
4669 }else if(opt.buttons.yes){
4670 handleButton("yes");
4674 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4675 textareaEl.enableDisplayMode();
4676 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4677 progressEl.enableDisplayMode();
4679 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4680 var pf = progressEl.dom.firstChild;
4682 pp = Roo.get(pf.firstChild);
4683 pp.setHeight(pf.offsetHeight);
4691 * Updates the message box body text
4692 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4693 * the XHTML-compliant non-breaking space character '&#160;')
4694 * @return {Roo.MessageBox} This message box
4696 updateText : function(text)
4698 if(!dlg.isVisible() && !opt.width){
4699 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4700 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4702 msgEl.innerHTML = text || ' ';
4704 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4705 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4707 Math.min(opt.width || cw , this.maxWidth),
4708 Math.max(opt.minWidth || this.minWidth, bwidth)
4711 activeTextEl.setWidth(w);
4713 if(dlg.isVisible()){
4714 dlg.fixedcenter = false;
4716 // to big, make it scroll. = But as usual stupid IE does not support
4719 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4720 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4721 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4723 bodyEl.dom.style.height = '';
4724 bodyEl.dom.style.overflowY = '';
4727 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4729 bodyEl.dom.style.overflowX = '';
4732 dlg.setContentSize(w, bodyEl.getHeight());
4733 if(dlg.isVisible()){
4734 dlg.fixedcenter = true;
4740 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4741 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4742 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4743 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4744 * @return {Roo.MessageBox} This message box
4746 updateProgress : function(value, text){
4748 this.updateText(text);
4751 if (pp) { // weird bug on my firefox - for some reason this is not defined
4752 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4753 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4759 * Returns true if the message box is currently displayed
4760 * @return {Boolean} True if the message box is visible, else false
4762 isVisible : function(){
4763 return dlg && dlg.isVisible();
4767 * Hides the message box if it is displayed
4770 if(this.isVisible()){
4776 * Displays a new message box, or reinitializes an existing message box, based on the config options
4777 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4778 * The following config object properties are supported:
4780 Property Type Description
4781 ---------- --------------- ------------------------------------------------------------------------------------
4782 animEl String/Element An id or Element from which the message box should animate as it opens and
4783 closes (defaults to undefined)
4784 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4785 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4786 closable Boolean False to hide the top-right close button (defaults to true). Note that
4787 progress and wait dialogs will ignore this property and always hide the
4788 close button as they can only be closed programmatically.
4789 cls String A custom CSS class to apply to the message box element
4790 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4791 displayed (defaults to 75)
4792 fn Function A callback function to execute after closing the dialog. The arguments to the
4793 function will be btn (the name of the button that was clicked, if applicable,
4794 e.g. "ok"), and text (the value of the active text field, if applicable).
4795 Progress and wait dialogs will ignore this option since they do not respond to
4796 user actions and can only be closed programmatically, so any required function
4797 should be called by the same code after it closes the dialog.
4798 icon String A CSS class that provides a background image to be used as an icon for
4799 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4800 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4801 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4802 modal Boolean False to allow user interaction with the page while the message box is
4803 displayed (defaults to true)
4804 msg String A string that will replace the existing message box body text (defaults
4805 to the XHTML-compliant non-breaking space character ' ')
4806 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4807 progress Boolean True to display a progress bar (defaults to false)
4808 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4809 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4810 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4811 title String The title text
4812 value String The string value to set into the active textbox element if displayed
4813 wait Boolean True to display a progress bar (defaults to false)
4814 width Number The width of the dialog in pixels
4821 msg: 'Please enter your address:',
4823 buttons: Roo.MessageBox.OKCANCEL,
4826 animEl: 'addAddressBtn'
4829 * @param {Object} config Configuration options
4830 * @return {Roo.MessageBox} This message box
4832 show : function(options)
4835 // this causes nightmares if you show one dialog after another
4836 // especially on callbacks..
4838 if(this.isVisible()){
4841 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4842 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4843 Roo.log("New Dialog Message:" + options.msg )
4844 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4845 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4848 var d = this.getDialog();
4850 d.setTitle(opt.title || " ");
4851 d.closeEl.setDisplayed(opt.closable !== false);
4852 activeTextEl = textboxEl;
4853 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4858 textareaEl.setHeight(typeof opt.multiline == "number" ?
4859 opt.multiline : this.defaultTextHeight);
4860 activeTextEl = textareaEl;
4869 progressEl.setDisplayed(opt.progress === true);
4871 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4873 this.updateProgress(0);
4874 activeTextEl.dom.value = opt.value || "";
4876 dlg.setDefaultButton(activeTextEl);
4878 var bs = opt.buttons;
4882 }else if(bs && bs.yes){
4883 db = buttons["yes"];
4885 dlg.setDefaultButton(db);
4887 bwidth = updateButtons(opt.buttons);
4888 this.updateText(opt.msg);
4890 d.el.addClass(opt.cls);
4892 d.proxyDrag = opt.proxyDrag === true;
4893 d.modal = opt.modal !== false;
4894 d.mask = opt.modal !== false ? mask : false;
4896 // force it to the end of the z-index stack so it gets a cursor in FF
4897 document.body.appendChild(dlg.el.dom);
4898 d.animateTarget = null;
4899 d.show(options.animEl);
4905 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4906 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4907 * and closing the message box when the process is complete.
4908 * @param {String} title The title bar text
4909 * @param {String} msg The message box body text
4910 * @return {Roo.MessageBox} This message box
4912 progress : function(title, msg){
4919 minWidth: this.minProgressWidth,
4926 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4927 * If a callback function is passed it will be called after the user clicks the button, and the
4928 * id of the button that was clicked will be passed as the only parameter to the callback
4929 * (could also be the top-right close button).
4930 * @param {String} title The title bar text
4931 * @param {String} msg The message box body text
4932 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4933 * @param {Object} scope (optional) The scope of the callback function
4934 * @return {Roo.MessageBox} This message box
4936 alert : function(title, msg, fn, scope)
4951 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4952 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4953 * You are responsible for closing the message box when the process is complete.
4954 * @param {String} msg The message box body text
4955 * @param {String} title (optional) The title bar text
4956 * @return {Roo.MessageBox} This message box
4958 wait : function(msg, title){
4969 waitTimer = Roo.TaskMgr.start({
4971 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4979 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4980 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4981 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4982 * @param {String} title The title bar text
4983 * @param {String} msg The message box body text
4984 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4985 * @param {Object} scope (optional) The scope of the callback function
4986 * @return {Roo.MessageBox} This message box
4988 confirm : function(title, msg, fn, scope){
4992 buttons: this.YESNO,
5001 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5002 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5003 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5004 * (could also be the top-right close button) and the text that was entered will be passed as the two
5005 * parameters to the callback.
5006 * @param {String} title The title bar text
5007 * @param {String} msg The message box body text
5008 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5009 * @param {Object} scope (optional) The scope of the callback function
5010 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5011 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5012 * @return {Roo.MessageBox} This message box
5014 prompt : function(title, msg, fn, scope, multiline){
5018 buttons: this.OKCANCEL,
5023 multiline: multiline,
5030 * Button config that displays a single OK button
5035 * Button config that displays Yes and No buttons
5038 YESNO : {yes:true, no:true},
5040 * Button config that displays OK and Cancel buttons
5043 OKCANCEL : {ok:true, cancel:true},
5045 * Button config that displays Yes, No and Cancel buttons
5048 YESNOCANCEL : {yes:true, no:true, cancel:true},
5051 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5054 defaultTextHeight : 75,
5056 * The maximum width in pixels of the message box (defaults to 600)
5061 * The minimum width in pixels of the message box (defaults to 100)
5066 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5067 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5070 minProgressWidth : 250,
5072 * An object containing the default button text strings that can be overriden for localized language support.
5073 * Supported properties are: ok, cancel, yes and no.
5074 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5087 * Shorthand for {@link Roo.MessageBox}
5089 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5090 Roo.Msg = Roo.Msg || Roo.MessageBox;
5099 * @class Roo.bootstrap.Navbar
5100 * @extends Roo.bootstrap.Component
5101 * Bootstrap Navbar class
5104 * Create a new Navbar
5105 * @param {Object} config The config object
5109 Roo.bootstrap.Navbar = function(config){
5110 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5114 * @event beforetoggle
5115 * Fire before toggle the menu
5116 * @param {Roo.EventObject} e
5118 "beforetoggle" : true
5122 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5131 getAutoCreate : function(){
5134 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5138 initEvents :function ()
5140 //Roo.log(this.el.select('.navbar-toggle',true));
5141 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5148 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5150 var size = this.el.getSize();
5151 this.maskEl.setSize(size.width, size.height);
5152 this.maskEl.enableDisplayMode("block");
5161 getChildContainer : function()
5163 if (this.el && this.el.select('.collapse').getCount()) {
5164 return this.el.select('.collapse',true).first();
5179 onToggle : function()
5182 if(this.fireEvent('beforetoggle', this) === false){
5185 var ce = this.el.select('.navbar-collapse',true).first();
5187 if (!ce.hasClass('show')) {
5197 * Expand the navbar pulldown
5199 expand : function ()
5202 var ce = this.el.select('.navbar-collapse',true).first();
5203 if (ce.hasClass('collapsing')) {
5206 ce.dom.style.height = '';
5208 ce.addClass('in'); // old...
5209 ce.removeClass('collapse');
5210 ce.addClass('show');
5211 var h = ce.getHeight();
5213 ce.removeClass('show');
5214 // at this point we should be able to see it..
5215 ce.addClass('collapsing');
5217 ce.setHeight(0); // resize it ...
5218 ce.on('transitionend', function() {
5219 //Roo.log('done transition');
5220 ce.removeClass('collapsing');
5221 ce.addClass('show');
5222 ce.removeClass('collapse');
5224 ce.dom.style.height = '';
5225 }, this, { single: true} );
5227 ce.dom.scrollTop = 0;
5230 * Collapse the navbar pulldown
5232 collapse : function()
5234 var ce = this.el.select('.navbar-collapse',true).first();
5236 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5237 // it's collapsed or collapsing..
5240 ce.removeClass('in'); // old...
5241 ce.setHeight(ce.getHeight());
5242 ce.removeClass('show');
5243 ce.addClass('collapsing');
5245 ce.on('transitionend', function() {
5246 ce.dom.style.height = '';
5247 ce.removeClass('collapsing');
5248 ce.addClass('collapse');
5249 }, this, { single: true} );
5269 * @class Roo.bootstrap.NavSimplebar
5270 * @extends Roo.bootstrap.Navbar
5271 * Bootstrap Sidebar class
5273 * @cfg {Boolean} inverse is inverted color
5275 * @cfg {String} type (nav | pills | tabs)
5276 * @cfg {Boolean} arrangement stacked | justified
5277 * @cfg {String} align (left | right) alignment
5279 * @cfg {Boolean} main (true|false) main nav bar? default false
5280 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5282 * @cfg {String} tag (header|footer|nav|div) default is nav
5284 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5288 * Create a new Sidebar
5289 * @param {Object} config The config object
5293 Roo.bootstrap.NavSimplebar = function(config){
5294 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5297 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5313 getAutoCreate : function(){
5317 tag : this.tag || 'div',
5318 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5320 if (['light','white'].indexOf(this.weight) > -1) {
5321 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5323 cfg.cls += ' bg-' + this.weight;
5326 cfg.cls += ' navbar-inverse';
5330 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5332 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5341 cls: 'nav nav-' + this.xtype,
5347 this.type = this.type || 'nav';
5348 if (['tabs','pills'].indexOf(this.type) != -1) {
5349 cfg.cn[0].cls += ' nav-' + this.type
5353 if (this.type!=='nav') {
5354 Roo.log('nav type must be nav/tabs/pills')
5356 cfg.cn[0].cls += ' navbar-nav'
5362 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5363 cfg.cn[0].cls += ' nav-' + this.arrangement;
5367 if (this.align === 'right') {
5368 cfg.cn[0].cls += ' navbar-right';
5393 * navbar-expand-md fixed-top
5397 * @class Roo.bootstrap.NavHeaderbar
5398 * @extends Roo.bootstrap.NavSimplebar
5399 * Bootstrap Sidebar class
5401 * @cfg {String} brand what is brand
5402 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5403 * @cfg {String} brand_href href of the brand
5404 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5405 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5406 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5407 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5410 * Create a new Sidebar
5411 * @param {Object} config The config object
5415 Roo.bootstrap.NavHeaderbar = function(config){
5416 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5420 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5427 desktopCenter : false,
5430 getAutoCreate : function(){
5433 tag: this.nav || 'nav',
5434 cls: 'navbar navbar-expand-md',
5440 if (this.desktopCenter) {
5441 cn.push({cls : 'container', cn : []});
5449 cls: 'navbar-toggle navbar-toggler',
5450 'data-toggle': 'collapse',
5455 html: 'Toggle navigation'
5459 cls: 'icon-bar navbar-toggler-icon'
5472 cn.push( Roo.bootstrap.version == 4 ? btn : {
5474 cls: 'navbar-header',
5483 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5487 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5489 if (['light','white'].indexOf(this.weight) > -1) {
5490 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5492 cfg.cls += ' bg-' + this.weight;
5495 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5496 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5498 // tag can override this..
5500 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5503 if (this.brand !== '') {
5504 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5505 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5507 href: this.brand_href ? this.brand_href : '#',
5508 cls: 'navbar-brand',
5516 cfg.cls += ' main-nav';
5524 getHeaderChildContainer : function()
5526 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5527 return this.el.select('.navbar-header',true).first();
5530 return this.getChildContainer();
5533 getChildContainer : function()
5536 return this.el.select('.roo-navbar-collapse',true).first();
5541 initEvents : function()
5543 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5545 if (this.autohide) {
5550 Roo.get(document).on('scroll',function(e) {
5551 var ns = Roo.get(document).getScroll().top;
5552 var os = prevScroll;
5556 ft.removeClass('slideDown');
5557 ft.addClass('slideUp');
5560 ft.removeClass('slideUp');
5561 ft.addClass('slideDown');
5582 * @class Roo.bootstrap.NavSidebar
5583 * @extends Roo.bootstrap.Navbar
5584 * Bootstrap Sidebar class
5587 * Create a new Sidebar
5588 * @param {Object} config The config object
5592 Roo.bootstrap.NavSidebar = function(config){
5593 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5596 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5598 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5600 getAutoCreate : function(){
5605 cls: 'sidebar sidebar-nav'
5627 * @class Roo.bootstrap.NavGroup
5628 * @extends Roo.bootstrap.Component
5629 * Bootstrap NavGroup class
5630 * @cfg {String} align (left|right)
5631 * @cfg {Boolean} inverse
5632 * @cfg {String} type (nav|pills|tab) default nav
5633 * @cfg {String} navId - reference Id for navbar.
5637 * Create a new nav group
5638 * @param {Object} config The config object
5641 Roo.bootstrap.NavGroup = function(config){
5642 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5645 Roo.bootstrap.NavGroup.register(this);
5649 * Fires when the active item changes
5650 * @param {Roo.bootstrap.NavGroup} this
5651 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5652 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5659 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5670 getAutoCreate : function()
5672 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5678 if (Roo.bootstrap.version == 4) {
5679 if (['tabs','pills'].indexOf(this.type) != -1) {
5680 cfg.cls += ' nav-' + this.type;
5682 // trying to remove so header bar can right align top?
5683 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5684 // do not use on header bar...
5685 cfg.cls += ' navbar-nav';
5690 if (['tabs','pills'].indexOf(this.type) != -1) {
5691 cfg.cls += ' nav-' + this.type
5693 if (this.type !== 'nav') {
5694 Roo.log('nav type must be nav/tabs/pills')
5696 cfg.cls += ' navbar-nav'
5700 if (this.parent() && this.parent().sidebar) {
5703 cls: 'dashboard-menu sidebar-menu'
5709 if (this.form === true) {
5712 cls: 'navbar-form form-inline'
5714 //nav navbar-right ml-md-auto
5715 if (this.align === 'right') {
5716 cfg.cls += ' navbar-right ml-md-auto';
5718 cfg.cls += ' navbar-left';
5722 if (this.align === 'right') {
5723 cfg.cls += ' navbar-right ml-md-auto';
5725 cfg.cls += ' mr-auto';
5729 cfg.cls += ' navbar-inverse';
5737 * sets the active Navigation item
5738 * @param {Roo.bootstrap.NavItem} the new current navitem
5740 setActiveItem : function(item)
5743 Roo.each(this.navItems, function(v){
5748 v.setActive(false, true);
5755 item.setActive(true, true);
5756 this.fireEvent('changed', this, item, prev);
5761 * gets the active Navigation item
5762 * @return {Roo.bootstrap.NavItem} the current navitem
5764 getActive : function()
5768 Roo.each(this.navItems, function(v){
5779 indexOfNav : function()
5783 Roo.each(this.navItems, function(v,i){
5794 * adds a Navigation item
5795 * @param {Roo.bootstrap.NavItem} the navitem to add
5797 addItem : function(cfg)
5799 if (this.form && Roo.bootstrap.version == 4) {
5802 var cn = new Roo.bootstrap.NavItem(cfg);
5804 cn.parentId = this.id;
5805 cn.onRender(this.el, null);
5809 * register a Navigation item
5810 * @param {Roo.bootstrap.NavItem} the navitem to add
5812 register : function(item)
5814 this.navItems.push( item);
5815 item.navId = this.navId;
5820 * clear all the Navigation item
5823 clearAll : function()
5826 this.el.dom.innerHTML = '';
5829 getNavItem: function(tabId)
5832 Roo.each(this.navItems, function(e) {
5833 if (e.tabId == tabId) {
5843 setActiveNext : function()
5845 var i = this.indexOfNav(this.getActive());
5846 if (i > this.navItems.length) {
5849 this.setActiveItem(this.navItems[i+1]);
5851 setActivePrev : function()
5853 var i = this.indexOfNav(this.getActive());
5857 this.setActiveItem(this.navItems[i-1]);
5859 clearWasActive : function(except) {
5860 Roo.each(this.navItems, function(e) {
5861 if (e.tabId != except.tabId && e.was_active) {
5862 e.was_active = false;
5869 getWasActive : function ()
5872 Roo.each(this.navItems, function(e) {
5887 Roo.apply(Roo.bootstrap.NavGroup, {
5891 * register a Navigation Group
5892 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5894 register : function(navgrp)
5896 this.groups[navgrp.navId] = navgrp;
5900 * fetch a Navigation Group based on the navigation ID
5901 * @param {string} the navgroup to add
5902 * @returns {Roo.bootstrap.NavGroup} the navgroup
5904 get: function(navId) {
5905 if (typeof(this.groups[navId]) == 'undefined') {
5907 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5909 return this.groups[navId] ;
5924 * @class Roo.bootstrap.NavItem
5925 * @extends Roo.bootstrap.Component
5926 * Bootstrap Navbar.NavItem class
5927 * @cfg {String} href link to
5928 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5930 * @cfg {String} html content of button
5931 * @cfg {String} badge text inside badge
5932 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5933 * @cfg {String} glyphicon DEPRICATED - use fa
5934 * @cfg {String} icon DEPRICATED - use fa
5935 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5936 * @cfg {Boolean} active Is item active
5937 * @cfg {Boolean} disabled Is item disabled
5939 * @cfg {Boolean} preventDefault (true | false) default false
5940 * @cfg {String} tabId the tab that this item activates.
5941 * @cfg {String} tagtype (a|span) render as a href or span?
5942 * @cfg {Boolean} animateRef (true|false) link to element default false
5945 * Create a new Navbar Item
5946 * @param {Object} config The config object
5948 Roo.bootstrap.NavItem = function(config){
5949 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5954 * The raw click event for the entire grid.
5955 * @param {Roo.EventObject} e
5960 * Fires when the active item active state changes
5961 * @param {Roo.bootstrap.NavItem} this
5962 * @param {boolean} state the new state
5968 * Fires when scroll to element
5969 * @param {Roo.bootstrap.NavItem} this
5970 * @param {Object} options
5971 * @param {Roo.EventObject} e
5979 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5988 preventDefault : false,
5996 button_outline : false,
6000 getAutoCreate : function(){
6008 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6010 if (this.disabled) {
6011 cfg.cls += ' disabled';
6015 if (this.button_weight.length) {
6016 cfg.tag = this.href ? 'a' : 'button';
6017 cfg.html = this.html || '';
6018 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6020 cfg.href = this.href;
6023 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6026 // menu .. should add dropdown-menu class - so no need for carat..
6028 if (this.badge !== '') {
6030 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6035 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6039 href : this.href || "#",
6040 html: this.html || ''
6043 if (this.tagtype == 'a') {
6044 cfg.cn[0].cls = 'nav-link';
6047 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6050 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6052 if(this.glyphicon) {
6053 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6058 cfg.cn[0].html += " <span class='caret'></span>";
6062 if (this.badge !== '') {
6064 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6072 onRender : function(ct, position)
6074 // Roo.log("Call onRender: " + this.xtype);
6075 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6079 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6080 this.navLink = this.el.select('.nav-link',true).first();
6085 initEvents: function()
6087 if (typeof (this.menu) != 'undefined') {
6088 this.menu.parentType = this.xtype;
6089 this.menu.triggerEl = this.el;
6090 this.menu = this.addxtype(Roo.apply({}, this.menu));
6093 this.el.select('a',true).on('click', this.onClick, this);
6095 if(this.tagtype == 'span'){
6096 this.el.select('span',true).on('click', this.onClick, this);
6099 // at this point parent should be available..
6100 this.parent().register(this);
6103 onClick : function(e)
6105 if (e.getTarget('.dropdown-menu-item')) {
6106 // did you click on a menu itemm.... - then don't trigger onclick..
6111 this.preventDefault ||
6114 Roo.log("NavItem - prevent Default?");
6118 if (this.disabled) {
6122 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6123 if (tg && tg.transition) {
6124 Roo.log("waiting for the transitionend");
6130 //Roo.log("fire event clicked");
6131 if(this.fireEvent('click', this, e) === false){
6135 if(this.tagtype == 'span'){
6139 //Roo.log(this.href);
6140 var ael = this.el.select('a',true).first();
6143 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6144 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6145 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6146 return; // ignore... - it's a 'hash' to another page.
6148 Roo.log("NavItem - prevent Default?");
6150 this.scrollToElement(e);
6154 var p = this.parent();
6156 if (['tabs','pills'].indexOf(p.type)!==-1) {
6157 if (typeof(p.setActiveItem) !== 'undefined') {
6158 p.setActiveItem(this);
6162 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6163 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6164 // remove the collapsed menu expand...
6165 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6169 isActive: function () {
6172 setActive : function(state, fire, is_was_active)
6174 if (this.active && !state && this.navId) {
6175 this.was_active = true;
6176 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6178 nv.clearWasActive(this);
6182 this.active = state;
6185 this.el.removeClass('active');
6186 this.navLink ? this.navLink.removeClass('active') : false;
6187 } else if (!this.el.hasClass('active')) {
6189 this.el.addClass('active');
6190 if (Roo.bootstrap.version == 4 && this.navLink ) {
6191 this.navLink.addClass('active');
6196 this.fireEvent('changed', this, state);
6199 // show a panel if it's registered and related..
6201 if (!this.navId || !this.tabId || !state || is_was_active) {
6205 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6209 var pan = tg.getPanelByName(this.tabId);
6213 // if we can not flip to new panel - go back to old nav highlight..
6214 if (false == tg.showPanel(pan)) {
6215 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6217 var onav = nv.getWasActive();
6219 onav.setActive(true, false, true);
6228 // this should not be here...
6229 setDisabled : function(state)
6231 this.disabled = state;
6233 this.el.removeClass('disabled');
6234 } else if (!this.el.hasClass('disabled')) {
6235 this.el.addClass('disabled');
6241 * Fetch the element to display the tooltip on.
6242 * @return {Roo.Element} defaults to this.el
6244 tooltipEl : function()
6246 return this.el.select('' + this.tagtype + '', true).first();
6249 scrollToElement : function(e)
6251 var c = document.body;
6254 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6256 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6257 c = document.documentElement;
6260 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6266 var o = target.calcOffsetsTo(c);
6273 this.fireEvent('scrollto', this, options, e);
6275 Roo.get(c).scrollTo('top', options.value, true);
6288 * <span> icon </span>
6289 * <span> text </span>
6290 * <span>badge </span>
6294 * @class Roo.bootstrap.NavSidebarItem
6295 * @extends Roo.bootstrap.NavItem
6296 * Bootstrap Navbar.NavSidebarItem class
6297 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6298 * {Boolean} open is the menu open
6299 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6300 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6301 * {String} buttonSize (sm|md|lg)the extra classes for the button
6302 * {Boolean} showArrow show arrow next to the text (default true)
6304 * Create a new Navbar Button
6305 * @param {Object} config The config object
6307 Roo.bootstrap.NavSidebarItem = function(config){
6308 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6313 * The raw click event for the entire grid.
6314 * @param {Roo.EventObject} e
6319 * Fires when the active item active state changes
6320 * @param {Roo.bootstrap.NavSidebarItem} this
6321 * @param {boolean} state the new state
6329 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6331 badgeWeight : 'default',
6337 buttonWeight : 'default',
6343 getAutoCreate : function(){
6348 href : this.href || '#',
6354 if(this.buttonView){
6357 href : this.href || '#',
6358 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6371 cfg.cls += ' active';
6374 if (this.disabled) {
6375 cfg.cls += ' disabled';
6378 cfg.cls += ' open x-open';
6381 if (this.glyphicon || this.icon) {
6382 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6383 a.cn.push({ tag : 'i', cls : c }) ;
6386 if(!this.buttonView){
6389 html : this.html || ''
6396 if (this.badge !== '') {
6397 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6403 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6406 a.cls += ' dropdown-toggle treeview' ;
6412 initEvents : function()
6414 if (typeof (this.menu) != 'undefined') {
6415 this.menu.parentType = this.xtype;
6416 this.menu.triggerEl = this.el;
6417 this.menu = this.addxtype(Roo.apply({}, this.menu));
6420 this.el.on('click', this.onClick, this);
6422 if(this.badge !== ''){
6423 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6428 onClick : function(e)
6435 if(this.preventDefault){
6439 this.fireEvent('click', this, e);
6442 disable : function()
6444 this.setDisabled(true);
6449 this.setDisabled(false);
6452 setDisabled : function(state)
6454 if(this.disabled == state){
6458 this.disabled = state;
6461 this.el.addClass('disabled');
6465 this.el.removeClass('disabled');
6470 setActive : function(state)
6472 if(this.active == state){
6476 this.active = state;
6479 this.el.addClass('active');
6483 this.el.removeClass('active');
6488 isActive: function ()
6493 setBadge : function(str)
6499 this.badgeEl.dom.innerHTML = str;
6516 * @class Roo.bootstrap.Row
6517 * @extends Roo.bootstrap.Component
6518 * Bootstrap Row class (contains columns...)
6522 * @param {Object} config The config object
6525 Roo.bootstrap.Row = function(config){
6526 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6529 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6531 getAutoCreate : function(){
6550 * @class Roo.bootstrap.Pagination
6551 * @extends Roo.bootstrap.Component
6552 * Bootstrap Pagination class
6553 * @cfg {String} size xs | sm | md | lg
6554 * @cfg {Boolean} inverse false | true
6557 * Create a new Pagination
6558 * @param {Object} config The config object
6561 Roo.bootstrap.Pagination = function(config){
6562 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6571 getAutoCreate : function(){
6577 cfg.cls += ' inverse';
6583 cfg.cls += " " + this.cls;
6601 * @class Roo.bootstrap.PaginationItem
6602 * @extends Roo.bootstrap.Component
6603 * Bootstrap PaginationItem class
6604 * @cfg {String} html text
6605 * @cfg {String} href the link
6606 * @cfg {Boolean} preventDefault (true | false) default true
6607 * @cfg {Boolean} active (true | false) default false
6608 * @cfg {Boolean} disabled default false
6612 * Create a new PaginationItem
6613 * @param {Object} config The config object
6617 Roo.bootstrap.PaginationItem = function(config){
6618 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6623 * The raw click event for the entire grid.
6624 * @param {Roo.EventObject} e
6630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6634 preventDefault: true,
6639 getAutoCreate : function(){
6645 href : this.href ? this.href : '#',
6646 html : this.html ? this.html : ''
6656 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6660 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6666 initEvents: function() {
6668 this.el.on('click', this.onClick, this);
6671 onClick : function(e)
6673 Roo.log('PaginationItem on click ');
6674 if(this.preventDefault){
6682 this.fireEvent('click', this, e);
6698 * @class Roo.bootstrap.Slider
6699 * @extends Roo.bootstrap.Component
6700 * Bootstrap Slider class
6703 * Create a new Slider
6704 * @param {Object} config The config object
6707 Roo.bootstrap.Slider = function(config){
6708 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6713 getAutoCreate : function(){
6717 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6721 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6733 * Ext JS Library 1.1.1
6734 * Copyright(c) 2006-2007, Ext JS, LLC.
6736 * Originally Released Under LGPL - original licence link has changed is not relivant.
6739 * <script type="text/javascript">
6744 * @class Roo.grid.ColumnModel
6745 * @extends Roo.util.Observable
6746 * This is the default implementation of a ColumnModel used by the Grid. It defines
6747 * the columns in the grid.
6750 var colModel = new Roo.grid.ColumnModel([
6751 {header: "Ticker", width: 60, sortable: true, locked: true},
6752 {header: "Company Name", width: 150, sortable: true},
6753 {header: "Market Cap.", width: 100, sortable: true},
6754 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6755 {header: "Employees", width: 100, sortable: true, resizable: false}
6760 * The config options listed for this class are options which may appear in each
6761 * individual column definition.
6762 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6764 * @param {Object} config An Array of column config objects. See this class's
6765 * config objects for details.
6767 Roo.grid.ColumnModel = function(config){
6769 * The config passed into the constructor
6771 this.config = config;
6774 // if no id, create one
6775 // if the column does not have a dataIndex mapping,
6776 // map it to the order it is in the config
6777 for(var i = 0, len = config.length; i < len; i++){
6779 if(typeof c.dataIndex == "undefined"){
6782 if(typeof c.renderer == "string"){
6783 c.renderer = Roo.util.Format[c.renderer];
6785 if(typeof c.id == "undefined"){
6788 if(c.editor && c.editor.xtype){
6789 c.editor = Roo.factory(c.editor, Roo.grid);
6791 if(c.editor && c.editor.isFormField){
6792 c.editor = new Roo.grid.GridEditor(c.editor);
6794 this.lookup[c.id] = c;
6798 * The width of columns which have no width specified (defaults to 100)
6801 this.defaultWidth = 100;
6804 * Default sortable of columns which have no sortable specified (defaults to false)
6807 this.defaultSortable = false;
6811 * @event widthchange
6812 * Fires when the width of a column changes.
6813 * @param {ColumnModel} this
6814 * @param {Number} columnIndex The column index
6815 * @param {Number} newWidth The new width
6817 "widthchange": true,
6819 * @event headerchange
6820 * Fires when the text of a header changes.
6821 * @param {ColumnModel} this
6822 * @param {Number} columnIndex The column index
6823 * @param {Number} newText The new header text
6825 "headerchange": true,
6827 * @event hiddenchange
6828 * Fires when a column is hidden or "unhidden".
6829 * @param {ColumnModel} this
6830 * @param {Number} columnIndex The column index
6831 * @param {Boolean} hidden true if hidden, false otherwise
6833 "hiddenchange": true,
6835 * @event columnmoved
6836 * Fires when a column is moved.
6837 * @param {ColumnModel} this
6838 * @param {Number} oldIndex
6839 * @param {Number} newIndex
6841 "columnmoved" : true,
6843 * @event columlockchange
6844 * Fires when a column's locked state is changed
6845 * @param {ColumnModel} this
6846 * @param {Number} colIndex
6847 * @param {Boolean} locked true if locked
6849 "columnlockchange" : true
6851 Roo.grid.ColumnModel.superclass.constructor.call(this);
6853 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6855 * @cfg {String} header The header text to display in the Grid view.
6858 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6859 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6860 * specified, the column's index is used as an index into the Record's data Array.
6863 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6864 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6867 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6868 * Defaults to the value of the {@link #defaultSortable} property.
6869 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6872 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6875 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6878 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6881 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6884 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6885 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6886 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6887 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6890 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6893 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6896 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6899 * @cfg {String} cursor (Optional)
6902 * @cfg {String} tooltip (Optional)
6905 * @cfg {Number} xs (Optional)
6908 * @cfg {Number} sm (Optional)
6911 * @cfg {Number} md (Optional)
6914 * @cfg {Number} lg (Optional)
6917 * Returns the id of the column at the specified index.
6918 * @param {Number} index The column index
6919 * @return {String} the id
6921 getColumnId : function(index){
6922 return this.config[index].id;
6926 * Returns the column for a specified id.
6927 * @param {String} id The column id
6928 * @return {Object} the column
6930 getColumnById : function(id){
6931 return this.lookup[id];
6936 * Returns the column for a specified dataIndex.
6937 * @param {String} dataIndex The column dataIndex
6938 * @return {Object|Boolean} the column or false if not found
6940 getColumnByDataIndex: function(dataIndex){
6941 var index = this.findColumnIndex(dataIndex);
6942 return index > -1 ? this.config[index] : false;
6946 * Returns the index for a specified column id.
6947 * @param {String} id The column id
6948 * @return {Number} the index, or -1 if not found
6950 getIndexById : function(id){
6951 for(var i = 0, len = this.config.length; i < len; i++){
6952 if(this.config[i].id == id){
6960 * Returns the index for a specified column dataIndex.
6961 * @param {String} dataIndex The column dataIndex
6962 * @return {Number} the index, or -1 if not found
6965 findColumnIndex : function(dataIndex){
6966 for(var i = 0, len = this.config.length; i < len; i++){
6967 if(this.config[i].dataIndex == dataIndex){
6975 moveColumn : function(oldIndex, newIndex){
6976 var c = this.config[oldIndex];
6977 this.config.splice(oldIndex, 1);
6978 this.config.splice(newIndex, 0, c);
6979 this.dataMap = null;
6980 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6983 isLocked : function(colIndex){
6984 return this.config[colIndex].locked === true;
6987 setLocked : function(colIndex, value, suppressEvent){
6988 if(this.isLocked(colIndex) == value){
6991 this.config[colIndex].locked = value;
6993 this.fireEvent("columnlockchange", this, colIndex, value);
6997 getTotalLockedWidth : function(){
6999 for(var i = 0; i < this.config.length; i++){
7000 if(this.isLocked(i) && !this.isHidden(i)){
7001 this.totalWidth += this.getColumnWidth(i);
7007 getLockedCount : function(){
7008 for(var i = 0, len = this.config.length; i < len; i++){
7009 if(!this.isLocked(i)){
7014 return this.config.length;
7018 * Returns the number of columns.
7021 getColumnCount : function(visibleOnly){
7022 if(visibleOnly === true){
7024 for(var i = 0, len = this.config.length; i < len; i++){
7025 if(!this.isHidden(i)){
7031 return this.config.length;
7035 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7036 * @param {Function} fn
7037 * @param {Object} scope (optional)
7038 * @return {Array} result
7040 getColumnsBy : function(fn, scope){
7042 for(var i = 0, len = this.config.length; i < len; i++){
7043 var c = this.config[i];
7044 if(fn.call(scope||this, c, i) === true){
7052 * Returns true if the specified column is sortable.
7053 * @param {Number} col The column index
7056 isSortable : function(col){
7057 if(typeof this.config[col].sortable == "undefined"){
7058 return this.defaultSortable;
7060 return this.config[col].sortable;
7064 * Returns the rendering (formatting) function defined for the column.
7065 * @param {Number} col The column index.
7066 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7068 getRenderer : function(col){
7069 if(!this.config[col].renderer){
7070 return Roo.grid.ColumnModel.defaultRenderer;
7072 return this.config[col].renderer;
7076 * Sets the rendering (formatting) function for a column.
7077 * @param {Number} col The column index
7078 * @param {Function} fn The function to use to process the cell's raw data
7079 * to return HTML markup for the grid view. The render function is called with
7080 * the following parameters:<ul>
7081 * <li>Data value.</li>
7082 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7083 * <li>css A CSS style string to apply to the table cell.</li>
7084 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7085 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7086 * <li>Row index</li>
7087 * <li>Column index</li>
7088 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7090 setRenderer : function(col, fn){
7091 this.config[col].renderer = fn;
7095 * Returns the width for the specified column.
7096 * @param {Number} col The column index
7099 getColumnWidth : function(col){
7100 return this.config[col].width * 1 || this.defaultWidth;
7104 * Sets the width for a column.
7105 * @param {Number} col The column index
7106 * @param {Number} width The new width
7108 setColumnWidth : function(col, width, suppressEvent){
7109 this.config[col].width = width;
7110 this.totalWidth = null;
7112 this.fireEvent("widthchange", this, col, width);
7117 * Returns the total width of all columns.
7118 * @param {Boolean} includeHidden True to include hidden column widths
7121 getTotalWidth : function(includeHidden){
7122 if(!this.totalWidth){
7123 this.totalWidth = 0;
7124 for(var i = 0, len = this.config.length; i < len; i++){
7125 if(includeHidden || !this.isHidden(i)){
7126 this.totalWidth += this.getColumnWidth(i);
7130 return this.totalWidth;
7134 * Returns the header for the specified column.
7135 * @param {Number} col The column index
7138 getColumnHeader : function(col){
7139 return this.config[col].header;
7143 * Sets the header for a column.
7144 * @param {Number} col The column index
7145 * @param {String} header The new header
7147 setColumnHeader : function(col, header){
7148 this.config[col].header = header;
7149 this.fireEvent("headerchange", this, col, header);
7153 * Returns the tooltip for the specified column.
7154 * @param {Number} col The column index
7157 getColumnTooltip : function(col){
7158 return this.config[col].tooltip;
7161 * Sets the tooltip for a column.
7162 * @param {Number} col The column index
7163 * @param {String} tooltip The new tooltip
7165 setColumnTooltip : function(col, tooltip){
7166 this.config[col].tooltip = tooltip;
7170 * Returns the dataIndex for the specified column.
7171 * @param {Number} col The column index
7174 getDataIndex : function(col){
7175 return this.config[col].dataIndex;
7179 * Sets the dataIndex for a column.
7180 * @param {Number} col The column index
7181 * @param {Number} dataIndex The new dataIndex
7183 setDataIndex : function(col, dataIndex){
7184 this.config[col].dataIndex = dataIndex;
7190 * Returns true if the cell is editable.
7191 * @param {Number} colIndex The column index
7192 * @param {Number} rowIndex The row index - this is nto actually used..?
7195 isCellEditable : function(colIndex, rowIndex){
7196 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7200 * Returns the editor defined for the cell/column.
7201 * return false or null to disable editing.
7202 * @param {Number} colIndex The column index
7203 * @param {Number} rowIndex The row index
7206 getCellEditor : function(colIndex, rowIndex){
7207 return this.config[colIndex].editor;
7211 * Sets if a column is editable.
7212 * @param {Number} col The column index
7213 * @param {Boolean} editable True if the column is editable
7215 setEditable : function(col, editable){
7216 this.config[col].editable = editable;
7221 * Returns true if the column is hidden.
7222 * @param {Number} colIndex The column index
7225 isHidden : function(colIndex){
7226 return this.config[colIndex].hidden;
7231 * Returns true if the column width cannot be changed
7233 isFixed : function(colIndex){
7234 return this.config[colIndex].fixed;
7238 * Returns true if the column can be resized
7241 isResizable : function(colIndex){
7242 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7245 * Sets if a column is hidden.
7246 * @param {Number} colIndex The column index
7247 * @param {Boolean} hidden True if the column is hidden
7249 setHidden : function(colIndex, hidden){
7250 this.config[colIndex].hidden = hidden;
7251 this.totalWidth = null;
7252 this.fireEvent("hiddenchange", this, colIndex, hidden);
7256 * Sets the editor for a column.
7257 * @param {Number} col The column index
7258 * @param {Object} editor The editor object
7260 setEditor : function(col, editor){
7261 this.config[col].editor = editor;
7265 Roo.grid.ColumnModel.defaultRenderer = function(value)
7267 if(typeof value == "object") {
7270 if(typeof value == "string" && value.length < 1){
7274 return String.format("{0}", value);
7277 // Alias for backwards compatibility
7278 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7281 * Ext JS Library 1.1.1
7282 * Copyright(c) 2006-2007, Ext JS, LLC.
7284 * Originally Released Under LGPL - original licence link has changed is not relivant.
7287 * <script type="text/javascript">
7291 * @class Roo.LoadMask
7292 * A simple utility class for generically masking elements while loading data. If the element being masked has
7293 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7294 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7295 * element's UpdateManager load indicator and will be destroyed after the initial load.
7297 * Create a new LoadMask
7298 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7299 * @param {Object} config The config object
7301 Roo.LoadMask = function(el, config){
7302 this.el = Roo.get(el);
7303 Roo.apply(this, config);
7305 this.store.on('beforeload', this.onBeforeLoad, this);
7306 this.store.on('load', this.onLoad, this);
7307 this.store.on('loadexception', this.onLoadException, this);
7308 this.removeMask = false;
7310 var um = this.el.getUpdateManager();
7311 um.showLoadIndicator = false; // disable the default indicator
7312 um.on('beforeupdate', this.onBeforeLoad, this);
7313 um.on('update', this.onLoad, this);
7314 um.on('failure', this.onLoad, this);
7315 this.removeMask = true;
7319 Roo.LoadMask.prototype = {
7321 * @cfg {Boolean} removeMask
7322 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7323 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7327 * The text to display in a centered loading message box (defaults to 'Loading...')
7331 * @cfg {String} msgCls
7332 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7334 msgCls : 'x-mask-loading',
7337 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7343 * Disables the mask to prevent it from being displayed
7345 disable : function(){
7346 this.disabled = true;
7350 * Enables the mask so that it can be displayed
7352 enable : function(){
7353 this.disabled = false;
7356 onLoadException : function()
7360 if (typeof(arguments[3]) != 'undefined') {
7361 Roo.MessageBox.alert("Error loading",arguments[3]);
7365 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7366 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7373 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7378 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7382 onBeforeLoad : function(){
7384 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7389 destroy : function(){
7391 this.store.un('beforeload', this.onBeforeLoad, this);
7392 this.store.un('load', this.onLoad, this);
7393 this.store.un('loadexception', this.onLoadException, this);
7395 var um = this.el.getUpdateManager();
7396 um.un('beforeupdate', this.onBeforeLoad, this);
7397 um.un('update', this.onLoad, this);
7398 um.un('failure', this.onLoad, this);
7409 * @class Roo.bootstrap.Table
7410 * @extends Roo.bootstrap.Component
7411 * Bootstrap Table class
7412 * @cfg {String} cls table class
7413 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7414 * @cfg {String} bgcolor Specifies the background color for a table
7415 * @cfg {Number} border Specifies whether the table cells should have borders or not
7416 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7417 * @cfg {Number} cellspacing Specifies the space between cells
7418 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7419 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7420 * @cfg {String} sortable Specifies that the table should be sortable
7421 * @cfg {String} summary Specifies a summary of the content of a table
7422 * @cfg {Number} width Specifies the width of a table
7423 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7425 * @cfg {boolean} striped Should the rows be alternative striped
7426 * @cfg {boolean} bordered Add borders to the table
7427 * @cfg {boolean} hover Add hover highlighting
7428 * @cfg {boolean} condensed Format condensed
7429 * @cfg {boolean} responsive Format condensed
7430 * @cfg {Boolean} loadMask (true|false) default false
7431 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7432 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7433 * @cfg {Boolean} rowSelection (true|false) default false
7434 * @cfg {Boolean} cellSelection (true|false) default false
7435 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7436 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7437 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7438 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7442 * Create a new Table
7443 * @param {Object} config The config object
7446 Roo.bootstrap.Table = function(config){
7447 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7452 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7453 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7454 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7455 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7457 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7459 this.sm.grid = this;
7460 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7461 this.sm = this.selModel;
7462 this.sm.xmodule = this.xmodule || false;
7465 if (this.cm && typeof(this.cm.config) == 'undefined') {
7466 this.colModel = new Roo.grid.ColumnModel(this.cm);
7467 this.cm = this.colModel;
7468 this.cm.xmodule = this.xmodule || false;
7471 this.store= Roo.factory(this.store, Roo.data);
7472 this.ds = this.store;
7473 this.ds.xmodule = this.xmodule || false;
7476 if (this.footer && this.store) {
7477 this.footer.dataSource = this.ds;
7478 this.footer = Roo.factory(this.footer);
7485 * Fires when a cell is clicked
7486 * @param {Roo.bootstrap.Table} this
7487 * @param {Roo.Element} el
7488 * @param {Number} rowIndex
7489 * @param {Number} columnIndex
7490 * @param {Roo.EventObject} e
7494 * @event celldblclick
7495 * Fires when a cell is double clicked
7496 * @param {Roo.bootstrap.Table} this
7497 * @param {Roo.Element} el
7498 * @param {Number} rowIndex
7499 * @param {Number} columnIndex
7500 * @param {Roo.EventObject} e
7502 "celldblclick" : true,
7505 * Fires when a row is clicked
7506 * @param {Roo.bootstrap.Table} this
7507 * @param {Roo.Element} el
7508 * @param {Number} rowIndex
7509 * @param {Roo.EventObject} e
7513 * @event rowdblclick
7514 * Fires when a row is double clicked
7515 * @param {Roo.bootstrap.Table} this
7516 * @param {Roo.Element} el
7517 * @param {Number} rowIndex
7518 * @param {Roo.EventObject} e
7520 "rowdblclick" : true,
7523 * Fires when a mouseover occur
7524 * @param {Roo.bootstrap.Table} this
7525 * @param {Roo.Element} el
7526 * @param {Number} rowIndex
7527 * @param {Number} columnIndex
7528 * @param {Roo.EventObject} e
7533 * Fires when a mouseout occur
7534 * @param {Roo.bootstrap.Table} this
7535 * @param {Roo.Element} el
7536 * @param {Number} rowIndex
7537 * @param {Number} columnIndex
7538 * @param {Roo.EventObject} e
7543 * Fires when a row is rendered, so you can change add a style to it.
7544 * @param {Roo.bootstrap.Table} this
7545 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7549 * @event rowsrendered
7550 * Fires when all the rows have been rendered
7551 * @param {Roo.bootstrap.Table} this
7553 'rowsrendered' : true,
7555 * @event contextmenu
7556 * The raw contextmenu event for the entire grid.
7557 * @param {Roo.EventObject} e
7559 "contextmenu" : true,
7561 * @event rowcontextmenu
7562 * Fires when a row is right clicked
7563 * @param {Roo.bootstrap.Table} this
7564 * @param {Number} rowIndex
7565 * @param {Roo.EventObject} e
7567 "rowcontextmenu" : true,
7569 * @event cellcontextmenu
7570 * Fires when a cell is right clicked
7571 * @param {Roo.bootstrap.Table} this
7572 * @param {Number} rowIndex
7573 * @param {Number} cellIndex
7574 * @param {Roo.EventObject} e
7576 "cellcontextmenu" : true,
7578 * @event headercontextmenu
7579 * Fires when a header is right clicked
7580 * @param {Roo.bootstrap.Table} this
7581 * @param {Number} columnIndex
7582 * @param {Roo.EventObject} e
7584 "headercontextmenu" : true
7588 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7614 rowSelection : false,
7615 cellSelection : false,
7618 // Roo.Element - the tbody
7620 // Roo.Element - thead element
7623 container: false, // used by gridpanel...
7629 auto_hide_footer : false,
7631 getAutoCreate : function()
7633 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7640 if (this.scrollBody) {
7641 cfg.cls += ' table-body-fixed';
7644 cfg.cls += ' table-striped';
7648 cfg.cls += ' table-hover';
7650 if (this.bordered) {
7651 cfg.cls += ' table-bordered';
7653 if (this.condensed) {
7654 cfg.cls += ' table-condensed';
7656 if (this.responsive) {
7657 cfg.cls += ' table-responsive';
7661 cfg.cls+= ' ' +this.cls;
7664 // this lot should be simplifed...
7677 ].forEach(function(k) {
7685 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7688 if(this.store || this.cm){
7689 if(this.headerShow){
7690 cfg.cn.push(this.renderHeader());
7693 cfg.cn.push(this.renderBody());
7695 if(this.footerShow){
7696 cfg.cn.push(this.renderFooter());
7698 // where does this come from?
7699 //cfg.cls+= ' TableGrid';
7702 return { cn : [ cfg ] };
7705 initEvents : function()
7707 if(!this.store || !this.cm){
7710 if (this.selModel) {
7711 this.selModel.initEvents();
7715 //Roo.log('initEvents with ds!!!!');
7717 this.mainBody = this.el.select('tbody', true).first();
7718 this.mainHead = this.el.select('thead', true).first();
7719 this.mainFoot = this.el.select('tfoot', true).first();
7725 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7726 e.on('click', _this.sort, _this);
7729 this.mainBody.on("click", this.onClick, this);
7730 this.mainBody.on("dblclick", this.onDblClick, this);
7732 // why is this done????? = it breaks dialogs??
7733 //this.parent().el.setStyle('position', 'relative');
7737 this.footer.parentId = this.id;
7738 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7741 this.el.select('tfoot tr td').first().addClass('hide');
7746 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7749 this.store.on('load', this.onLoad, this);
7750 this.store.on('beforeload', this.onBeforeLoad, this);
7751 this.store.on('update', this.onUpdate, this);
7752 this.store.on('add', this.onAdd, this);
7753 this.store.on("clear", this.clear, this);
7755 this.el.on("contextmenu", this.onContextMenu, this);
7757 this.mainBody.on('scroll', this.onBodyScroll, this);
7759 this.cm.on("headerchange", this.onHeaderChange, this);
7761 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7765 onContextMenu : function(e, t)
7767 this.processEvent("contextmenu", e);
7770 processEvent : function(name, e)
7772 if (name != 'touchstart' ) {
7773 this.fireEvent(name, e);
7776 var t = e.getTarget();
7778 var cell = Roo.get(t);
7784 if(cell.findParent('tfoot', false, true)){
7788 if(cell.findParent('thead', false, true)){
7790 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7791 cell = Roo.get(t).findParent('th', false, true);
7793 Roo.log("failed to find th in thead?");
7794 Roo.log(e.getTarget());
7799 var cellIndex = cell.dom.cellIndex;
7801 var ename = name == 'touchstart' ? 'click' : name;
7802 this.fireEvent("header" + ename, this, cellIndex, e);
7807 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7808 cell = Roo.get(t).findParent('td', false, true);
7810 Roo.log("failed to find th in tbody?");
7811 Roo.log(e.getTarget());
7816 var row = cell.findParent('tr', false, true);
7817 var cellIndex = cell.dom.cellIndex;
7818 var rowIndex = row.dom.rowIndex - 1;
7822 this.fireEvent("row" + name, this, rowIndex, e);
7826 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7832 onMouseover : function(e, el)
7834 var cell = Roo.get(el);
7840 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7841 cell = cell.findParent('td', false, true);
7844 var row = cell.findParent('tr', false, true);
7845 var cellIndex = cell.dom.cellIndex;
7846 var rowIndex = row.dom.rowIndex - 1; // start from 0
7848 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7852 onMouseout : function(e, el)
7854 var cell = Roo.get(el);
7860 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7861 cell = cell.findParent('td', false, true);
7864 var row = cell.findParent('tr', false, true);
7865 var cellIndex = cell.dom.cellIndex;
7866 var rowIndex = row.dom.rowIndex - 1; // start from 0
7868 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7872 onClick : function(e, el)
7874 var cell = Roo.get(el);
7876 if(!cell || (!this.cellSelection && !this.rowSelection)){
7880 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7881 cell = cell.findParent('td', false, true);
7884 if(!cell || typeof(cell) == 'undefined'){
7888 var row = cell.findParent('tr', false, true);
7890 if(!row || typeof(row) == 'undefined'){
7894 var cellIndex = cell.dom.cellIndex;
7895 var rowIndex = this.getRowIndex(row);
7897 // why??? - should these not be based on SelectionModel?
7898 if(this.cellSelection){
7899 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7902 if(this.rowSelection){
7903 this.fireEvent('rowclick', this, row, rowIndex, e);
7909 onDblClick : function(e,el)
7911 var cell = Roo.get(el);
7913 if(!cell || (!this.cellSelection && !this.rowSelection)){
7917 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7918 cell = cell.findParent('td', false, true);
7921 if(!cell || typeof(cell) == 'undefined'){
7925 var row = cell.findParent('tr', false, true);
7927 if(!row || typeof(row) == 'undefined'){
7931 var cellIndex = cell.dom.cellIndex;
7932 var rowIndex = this.getRowIndex(row);
7934 if(this.cellSelection){
7935 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7938 if(this.rowSelection){
7939 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7943 sort : function(e,el)
7945 var col = Roo.get(el);
7947 if(!col.hasClass('sortable')){
7951 var sort = col.attr('sort');
7954 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7958 this.store.sortInfo = {field : sort, direction : dir};
7961 Roo.log("calling footer first");
7962 this.footer.onClick('first');
7965 this.store.load({ params : { start : 0 } });
7969 renderHeader : function()
7977 this.totalWidth = 0;
7979 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7981 var config = cm.config[i];
7985 cls : 'x-hcol-' + i,
7987 html: cm.getColumnHeader(i)
7992 if(typeof(config.sortable) != 'undefined' && config.sortable){
7994 c.html = '<i class="glyphicon"></i>' + c.html;
7997 // could use BS4 hidden-..-down
7999 if(typeof(config.lgHeader) != 'undefined'){
8000 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8003 if(typeof(config.mdHeader) != 'undefined'){
8004 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8007 if(typeof(config.smHeader) != 'undefined'){
8008 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8011 if(typeof(config.xsHeader) != 'undefined'){
8012 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8019 if(typeof(config.tooltip) != 'undefined'){
8020 c.tooltip = config.tooltip;
8023 if(typeof(config.colspan) != 'undefined'){
8024 c.colspan = config.colspan;
8027 if(typeof(config.hidden) != 'undefined' && config.hidden){
8028 c.style += ' display:none;';
8031 if(typeof(config.dataIndex) != 'undefined'){
8032 c.sort = config.dataIndex;
8037 if(typeof(config.align) != 'undefined' && config.align.length){
8038 c.style += ' text-align:' + config.align + ';';
8041 if(typeof(config.width) != 'undefined'){
8042 c.style += ' width:' + config.width + 'px;';
8043 this.totalWidth += config.width;
8045 this.totalWidth += 100; // assume minimum of 100 per column?
8048 if(typeof(config.cls) != 'undefined'){
8049 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8052 ['xs','sm','md','lg'].map(function(size){
8054 if(typeof(config[size]) == 'undefined'){
8058 if (!config[size]) { // 0 = hidden
8059 // BS 4 '0' is treated as hide that column and below.
8060 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8064 c.cls += ' col-' + size + '-' + config[size] + (
8065 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8077 renderBody : function()
8087 colspan : this.cm.getColumnCount()
8097 renderFooter : function()
8107 colspan : this.cm.getColumnCount()
8121 // Roo.log('ds onload');
8126 var ds = this.store;
8128 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8129 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8130 if (_this.store.sortInfo) {
8132 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8133 e.select('i', true).addClass(['glyphicon-arrow-up']);
8136 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8137 e.select('i', true).addClass(['glyphicon-arrow-down']);
8142 var tbody = this.mainBody;
8144 if(ds.getCount() > 0){
8145 ds.data.each(function(d,rowIndex){
8146 var row = this.renderRow(cm, ds, rowIndex);
8148 tbody.createChild(row);
8152 if(row.cellObjects.length){
8153 Roo.each(row.cellObjects, function(r){
8154 _this.renderCellObject(r);
8161 var tfoot = this.el.select('tfoot', true).first();
8163 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8165 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8167 var total = this.ds.getTotalCount();
8169 if(this.footer.pageSize < total){
8170 this.mainFoot.show();
8174 Roo.each(this.el.select('tbody td', true).elements, function(e){
8175 e.on('mouseover', _this.onMouseover, _this);
8178 Roo.each(this.el.select('tbody td', true).elements, function(e){
8179 e.on('mouseout', _this.onMouseout, _this);
8181 this.fireEvent('rowsrendered', this);
8187 onUpdate : function(ds,record)
8189 this.refreshRow(record);
8193 onRemove : function(ds, record, index, isUpdate){
8194 if(isUpdate !== true){
8195 this.fireEvent("beforerowremoved", this, index, record);
8197 var bt = this.mainBody.dom;
8199 var rows = this.el.select('tbody > tr', true).elements;
8201 if(typeof(rows[index]) != 'undefined'){
8202 bt.removeChild(rows[index].dom);
8205 // if(bt.rows[index]){
8206 // bt.removeChild(bt.rows[index]);
8209 if(isUpdate !== true){
8210 //this.stripeRows(index);
8211 //this.syncRowHeights(index, index);
8213 this.fireEvent("rowremoved", this, index, record);
8217 onAdd : function(ds, records, rowIndex)
8219 //Roo.log('on Add called');
8220 // - note this does not handle multiple adding very well..
8221 var bt = this.mainBody.dom;
8222 for (var i =0 ; i < records.length;i++) {
8223 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8224 //Roo.log(records[i]);
8225 //Roo.log(this.store.getAt(rowIndex+i));
8226 this.insertRow(this.store, rowIndex + i, false);
8233 refreshRow : function(record){
8234 var ds = this.store, index;
8235 if(typeof record == 'number'){
8237 record = ds.getAt(index);
8239 index = ds.indexOf(record);
8241 this.insertRow(ds, index, true);
8243 this.onRemove(ds, record, index+1, true);
8245 //this.syncRowHeights(index, index);
8247 this.fireEvent("rowupdated", this, index, record);
8250 insertRow : function(dm, rowIndex, isUpdate){
8253 this.fireEvent("beforerowsinserted", this, rowIndex);
8255 //var s = this.getScrollState();
8256 var row = this.renderRow(this.cm, this.store, rowIndex);
8257 // insert before rowIndex..
8258 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8262 if(row.cellObjects.length){
8263 Roo.each(row.cellObjects, function(r){
8264 _this.renderCellObject(r);
8269 this.fireEvent("rowsinserted", this, rowIndex);
8270 //this.syncRowHeights(firstRow, lastRow);
8271 //this.stripeRows(firstRow);
8278 getRowDom : function(rowIndex)
8280 var rows = this.el.select('tbody > tr', true).elements;
8282 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8285 // returns the object tree for a tr..
8288 renderRow : function(cm, ds, rowIndex)
8290 var d = ds.getAt(rowIndex);
8294 cls : 'x-row-' + rowIndex,
8298 var cellObjects = [];
8300 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8301 var config = cm.config[i];
8303 var renderer = cm.getRenderer(i);
8307 if(typeof(renderer) !== 'undefined'){
8308 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8310 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8311 // and are rendered into the cells after the row is rendered - using the id for the element.
8313 if(typeof(value) === 'object'){
8323 rowIndex : rowIndex,
8328 this.fireEvent('rowclass', this, rowcfg);
8332 cls : rowcfg.rowClass + ' x-col-' + i,
8334 html: (typeof(value) === 'object') ? '' : value
8341 if(typeof(config.colspan) != 'undefined'){
8342 td.colspan = config.colspan;
8345 if(typeof(config.hidden) != 'undefined' && config.hidden){
8346 td.style += ' display:none;';
8349 if(typeof(config.align) != 'undefined' && config.align.length){
8350 td.style += ' text-align:' + config.align + ';';
8352 if(typeof(config.valign) != 'undefined' && config.valign.length){
8353 td.style += ' vertical-align:' + config.valign + ';';
8356 if(typeof(config.width) != 'undefined'){
8357 td.style += ' width:' + config.width + 'px;';
8360 if(typeof(config.cursor) != 'undefined'){
8361 td.style += ' cursor:' + config.cursor + ';';
8364 if(typeof(config.cls) != 'undefined'){
8365 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8368 ['xs','sm','md','lg'].map(function(size){
8370 if(typeof(config[size]) == 'undefined'){
8376 if (!config[size]) { // 0 = hidden
8377 // BS 4 '0' is treated as hide that column and below.
8378 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8382 td.cls += ' col-' + size + '-' + config[size] + (
8383 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8393 row.cellObjects = cellObjects;
8401 onBeforeLoad : function()
8410 this.el.select('tbody', true).first().dom.innerHTML = '';
8413 * Show or hide a row.
8414 * @param {Number} rowIndex to show or hide
8415 * @param {Boolean} state hide
8417 setRowVisibility : function(rowIndex, state)
8419 var bt = this.mainBody.dom;
8421 var rows = this.el.select('tbody > tr', true).elements;
8423 if(typeof(rows[rowIndex]) == 'undefined'){
8426 rows[rowIndex].dom.style.display = state ? '' : 'none';
8430 getSelectionModel : function(){
8432 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8434 return this.selModel;
8437 * Render the Roo.bootstrap object from renderder
8439 renderCellObject : function(r)
8443 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8445 var t = r.cfg.render(r.container);
8448 Roo.each(r.cfg.cn, function(c){
8450 container: t.getChildContainer(),
8453 _this.renderCellObject(child);
8458 getRowIndex : function(row)
8462 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8473 * Returns the grid's underlying element = used by panel.Grid
8474 * @return {Element} The element
8476 getGridEl : function(){
8480 * Forces a resize - used by panel.Grid
8481 * @return {Element} The element
8483 autoSize : function()
8485 //var ctr = Roo.get(this.container.dom.parentElement);
8486 var ctr = Roo.get(this.el.dom);
8488 var thd = this.getGridEl().select('thead',true).first();
8489 var tbd = this.getGridEl().select('tbody', true).first();
8490 var tfd = this.getGridEl().select('tfoot', true).first();
8492 var cw = ctr.getWidth();
8496 tbd.setWidth(ctr.getWidth());
8497 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8498 // this needs fixing for various usage - currently only hydra job advers I think..
8500 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8502 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8505 cw = Math.max(cw, this.totalWidth);
8506 this.getGridEl().select('tr',true).setWidth(cw);
8507 // resize 'expandable coloumn?
8509 return; // we doe not have a view in this design..
8512 onBodyScroll: function()
8514 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8516 this.mainHead.setStyle({
8517 'position' : 'relative',
8518 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8524 var scrollHeight = this.mainBody.dom.scrollHeight;
8526 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8528 var height = this.mainBody.getHeight();
8530 if(scrollHeight - height == scrollTop) {
8532 var total = this.ds.getTotalCount();
8534 if(this.footer.cursor + this.footer.pageSize < total){
8536 this.footer.ds.load({
8538 start : this.footer.cursor + this.footer.pageSize,
8539 limit : this.footer.pageSize
8549 onHeaderChange : function()
8551 var header = this.renderHeader();
8552 var table = this.el.select('table', true).first();
8554 this.mainHead.remove();
8555 this.mainHead = table.createChild(header, this.mainBody, false);
8558 onHiddenChange : function(colModel, colIndex, hidden)
8560 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8561 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8563 this.CSS.updateRule(thSelector, "display", "");
8564 this.CSS.updateRule(tdSelector, "display", "");
8567 this.CSS.updateRule(thSelector, "display", "none");
8568 this.CSS.updateRule(tdSelector, "display", "none");
8571 this.onHeaderChange();
8575 setColumnWidth: function(col_index, width)
8577 // width = "md-2 xs-2..."
8578 if(!this.colModel.config[col_index]) {
8582 var w = width.split(" ");
8584 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8586 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8589 for(var j = 0; j < w.length; j++) {
8595 var size_cls = w[j].split("-");
8597 if(!Number.isInteger(size_cls[1] * 1)) {
8601 if(!this.colModel.config[col_index][size_cls[0]]) {
8605 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8609 h_row[0].classList.replace(
8610 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8611 "col-"+size_cls[0]+"-"+size_cls[1]
8614 for(var i = 0; i < rows.length; i++) {
8616 var size_cls = w[j].split("-");
8618 if(!Number.isInteger(size_cls[1] * 1)) {
8622 if(!this.colModel.config[col_index][size_cls[0]]) {
8626 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8630 rows[i].classList.replace(
8631 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8632 "col-"+size_cls[0]+"-"+size_cls[1]
8636 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8651 * @class Roo.bootstrap.TableCell
8652 * @extends Roo.bootstrap.Component
8653 * Bootstrap TableCell class
8654 * @cfg {String} html cell contain text
8655 * @cfg {String} cls cell class
8656 * @cfg {String} tag cell tag (td|th) default td
8657 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8658 * @cfg {String} align Aligns the content in a cell
8659 * @cfg {String} axis Categorizes cells
8660 * @cfg {String} bgcolor Specifies the background color of a cell
8661 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8662 * @cfg {Number} colspan Specifies the number of columns a cell should span
8663 * @cfg {String} headers Specifies one or more header cells a cell is related to
8664 * @cfg {Number} height Sets the height of a cell
8665 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8666 * @cfg {Number} rowspan Sets the number of rows a cell should span
8667 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8668 * @cfg {String} valign Vertical aligns the content in a cell
8669 * @cfg {Number} width Specifies the width of a cell
8672 * Create a new TableCell
8673 * @param {Object} config The config object
8676 Roo.bootstrap.TableCell = function(config){
8677 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8680 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8700 getAutoCreate : function(){
8701 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8721 cfg.align=this.align
8727 cfg.bgcolor=this.bgcolor
8730 cfg.charoff=this.charoff
8733 cfg.colspan=this.colspan
8736 cfg.headers=this.headers
8739 cfg.height=this.height
8742 cfg.nowrap=this.nowrap
8745 cfg.rowspan=this.rowspan
8748 cfg.scope=this.scope
8751 cfg.valign=this.valign
8754 cfg.width=this.width
8773 * @class Roo.bootstrap.TableRow
8774 * @extends Roo.bootstrap.Component
8775 * Bootstrap TableRow class
8776 * @cfg {String} cls row class
8777 * @cfg {String} align Aligns the content in a table row
8778 * @cfg {String} bgcolor Specifies a background color for a table row
8779 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8780 * @cfg {String} valign Vertical aligns the content in a table row
8783 * Create a new TableRow
8784 * @param {Object} config The config object
8787 Roo.bootstrap.TableRow = function(config){
8788 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8791 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8799 getAutoCreate : function(){
8800 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8810 cfg.align = this.align;
8813 cfg.bgcolor = this.bgcolor;
8816 cfg.charoff = this.charoff;
8819 cfg.valign = this.valign;
8837 * @class Roo.bootstrap.TableBody
8838 * @extends Roo.bootstrap.Component
8839 * Bootstrap TableBody class
8840 * @cfg {String} cls element class
8841 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8842 * @cfg {String} align Aligns the content inside the element
8843 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8844 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8847 * Create a new TableBody
8848 * @param {Object} config The config object
8851 Roo.bootstrap.TableBody = function(config){
8852 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8855 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8863 getAutoCreate : function(){
8864 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8878 cfg.align = this.align;
8881 cfg.charoff = this.charoff;
8884 cfg.valign = this.valign;
8891 // initEvents : function()
8898 // this.store = Roo.factory(this.store, Roo.data);
8899 // this.store.on('load', this.onLoad, this);
8901 // this.store.load();
8905 // onLoad: function ()
8907 // this.fireEvent('load', this);
8917 * Ext JS Library 1.1.1
8918 * Copyright(c) 2006-2007, Ext JS, LLC.
8920 * Originally Released Under LGPL - original licence link has changed is not relivant.
8923 * <script type="text/javascript">
8926 // as we use this in bootstrap.
8927 Roo.namespace('Roo.form');
8929 * @class Roo.form.Action
8930 * Internal Class used to handle form actions
8932 * @param {Roo.form.BasicForm} el The form element or its id
8933 * @param {Object} config Configuration options
8938 // define the action interface
8939 Roo.form.Action = function(form, options){
8941 this.options = options || {};
8944 * Client Validation Failed
8947 Roo.form.Action.CLIENT_INVALID = 'client';
8949 * Server Validation Failed
8952 Roo.form.Action.SERVER_INVALID = 'server';
8954 * Connect to Server Failed
8957 Roo.form.Action.CONNECT_FAILURE = 'connect';
8959 * Reading Data from Server Failed
8962 Roo.form.Action.LOAD_FAILURE = 'load';
8964 Roo.form.Action.prototype = {
8966 failureType : undefined,
8967 response : undefined,
8971 run : function(options){
8976 success : function(response){
8981 handleResponse : function(response){
8985 // default connection failure
8986 failure : function(response){
8988 this.response = response;
8989 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8990 this.form.afterAction(this, false);
8993 processResponse : function(response){
8994 this.response = response;
8995 if(!response.responseText){
8998 this.result = this.handleResponse(response);
9002 // utility functions used internally
9003 getUrl : function(appendParams){
9004 var url = this.options.url || this.form.url || this.form.el.dom.action;
9006 var p = this.getParams();
9008 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9014 getMethod : function(){
9015 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9018 getParams : function(){
9019 var bp = this.form.baseParams;
9020 var p = this.options.params;
9022 if(typeof p == "object"){
9023 p = Roo.urlEncode(Roo.applyIf(p, bp));
9024 }else if(typeof p == 'string' && bp){
9025 p += '&' + Roo.urlEncode(bp);
9028 p = Roo.urlEncode(bp);
9033 createCallback : function(){
9035 success: this.success,
9036 failure: this.failure,
9038 timeout: (this.form.timeout*1000),
9039 upload: this.form.fileUpload ? this.success : undefined
9044 Roo.form.Action.Submit = function(form, options){
9045 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9048 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9051 haveProgress : false,
9052 uploadComplete : false,
9054 // uploadProgress indicator.
9055 uploadProgress : function()
9057 if (!this.form.progressUrl) {
9061 if (!this.haveProgress) {
9062 Roo.MessageBox.progress("Uploading", "Uploading");
9064 if (this.uploadComplete) {
9065 Roo.MessageBox.hide();
9069 this.haveProgress = true;
9071 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9073 var c = new Roo.data.Connection();
9075 url : this.form.progressUrl,
9080 success : function(req){
9081 //console.log(data);
9085 rdata = Roo.decode(req.responseText)
9087 Roo.log("Invalid data from server..");
9091 if (!rdata || !rdata.success) {
9093 Roo.MessageBox.alert(Roo.encode(rdata));
9096 var data = rdata.data;
9098 if (this.uploadComplete) {
9099 Roo.MessageBox.hide();
9104 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9105 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9108 this.uploadProgress.defer(2000,this);
9111 failure: function(data) {
9112 Roo.log('progress url failed ');
9123 // run get Values on the form, so it syncs any secondary forms.
9124 this.form.getValues();
9126 var o = this.options;
9127 var method = this.getMethod();
9128 var isPost = method == 'POST';
9129 if(o.clientValidation === false || this.form.isValid()){
9131 if (this.form.progressUrl) {
9132 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9133 (new Date() * 1) + '' + Math.random());
9138 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9139 form:this.form.el.dom,
9140 url:this.getUrl(!isPost),
9142 params:isPost ? this.getParams() : null,
9143 isUpload: this.form.fileUpload,
9144 formData : this.form.formData
9147 this.uploadProgress();
9149 }else if (o.clientValidation !== false){ // client validation failed
9150 this.failureType = Roo.form.Action.CLIENT_INVALID;
9151 this.form.afterAction(this, false);
9155 success : function(response)
9157 this.uploadComplete= true;
9158 if (this.haveProgress) {
9159 Roo.MessageBox.hide();
9163 var result = this.processResponse(response);
9164 if(result === true || result.success){
9165 this.form.afterAction(this, true);
9169 this.form.markInvalid(result.errors);
9170 this.failureType = Roo.form.Action.SERVER_INVALID;
9172 this.form.afterAction(this, false);
9174 failure : function(response)
9176 this.uploadComplete= true;
9177 if (this.haveProgress) {
9178 Roo.MessageBox.hide();
9181 this.response = response;
9182 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9183 this.form.afterAction(this, false);
9186 handleResponse : function(response){
9187 if(this.form.errorReader){
9188 var rs = this.form.errorReader.read(response);
9191 for(var i = 0, len = rs.records.length; i < len; i++) {
9192 var r = rs.records[i];
9196 if(errors.length < 1){
9200 success : rs.success,
9206 ret = Roo.decode(response.responseText);
9210 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9220 Roo.form.Action.Load = function(form, options){
9221 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9222 this.reader = this.form.reader;
9225 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9230 Roo.Ajax.request(Roo.apply(
9231 this.createCallback(), {
9232 method:this.getMethod(),
9233 url:this.getUrl(false),
9234 params:this.getParams()
9238 success : function(response){
9240 var result = this.processResponse(response);
9241 if(result === true || !result.success || !result.data){
9242 this.failureType = Roo.form.Action.LOAD_FAILURE;
9243 this.form.afterAction(this, false);
9246 this.form.clearInvalid();
9247 this.form.setValues(result.data);
9248 this.form.afterAction(this, true);
9251 handleResponse : function(response){
9252 if(this.form.reader){
9253 var rs = this.form.reader.read(response);
9254 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9256 success : rs.success,
9260 return Roo.decode(response.responseText);
9264 Roo.form.Action.ACTION_TYPES = {
9265 'load' : Roo.form.Action.Load,
9266 'submit' : Roo.form.Action.Submit
9275 * @class Roo.bootstrap.Form
9276 * @extends Roo.bootstrap.Component
9277 * Bootstrap Form class
9278 * @cfg {String} method GET | POST (default POST)
9279 * @cfg {String} labelAlign top | left (default top)
9280 * @cfg {String} align left | right - for navbars
9281 * @cfg {Boolean} loadMask load mask when submit (default true)
9286 * @param {Object} config The config object
9290 Roo.bootstrap.Form = function(config){
9292 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9294 Roo.bootstrap.Form.popover.apply();
9298 * @event clientvalidation
9299 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9300 * @param {Form} this
9301 * @param {Boolean} valid true if the form has passed client-side validation
9303 clientvalidation: true,
9305 * @event beforeaction
9306 * Fires before any action is performed. Return false to cancel the action.
9307 * @param {Form} this
9308 * @param {Action} action The action to be performed
9312 * @event actionfailed
9313 * Fires when an action fails.
9314 * @param {Form} this
9315 * @param {Action} action The action that failed
9317 actionfailed : true,
9319 * @event actioncomplete
9320 * Fires when an action is completed.
9321 * @param {Form} this
9322 * @param {Action} action The action that completed
9324 actioncomplete : true
9328 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9331 * @cfg {String} method
9332 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9337 * The URL to use for form actions if one isn't supplied in the action options.
9340 * @cfg {Boolean} fileUpload
9341 * Set to true if this form is a file upload.
9345 * @cfg {Object} baseParams
9346 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9350 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9354 * @cfg {Sting} align (left|right) for navbar forms
9359 activeAction : null,
9362 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9363 * element by passing it or its id or mask the form itself by passing in true.
9366 waitMsgTarget : false,
9371 * @cfg {Boolean} errorMask (true|false) default false
9376 * @cfg {Number} maskOffset Default 100
9381 * @cfg {Boolean} maskBody
9385 getAutoCreate : function(){
9389 method : this.method || 'POST',
9390 id : this.id || Roo.id(),
9393 if (this.parent().xtype.match(/^Nav/)) {
9394 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9398 if (this.labelAlign == 'left' ) {
9399 cfg.cls += ' form-horizontal';
9405 initEvents : function()
9407 this.el.on('submit', this.onSubmit, this);
9408 // this was added as random key presses on the form where triggering form submit.
9409 this.el.on('keypress', function(e) {
9410 if (e.getCharCode() != 13) {
9413 // we might need to allow it for textareas.. and some other items.
9414 // check e.getTarget().
9416 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9420 Roo.log("keypress blocked");
9428 onSubmit : function(e){
9433 * Returns true if client-side validation on the form is successful.
9436 isValid : function(){
9437 var items = this.getItems();
9441 items.each(function(f){
9447 Roo.log('invalid field: ' + f.name);
9451 if(!target && f.el.isVisible(true)){
9457 if(this.errorMask && !valid){
9458 Roo.bootstrap.Form.popover.mask(this, target);
9465 * Returns true if any fields in this form have changed since their original load.
9468 isDirty : function(){
9470 var items = this.getItems();
9471 items.each(function(f){
9481 * Performs a predefined action (submit or load) or custom actions you define on this form.
9482 * @param {String} actionName The name of the action type
9483 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9484 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9485 * accept other config options):
9487 Property Type Description
9488 ---------------- --------------- ----------------------------------------------------------------------------------
9489 url String The url for the action (defaults to the form's url)
9490 method String The form method to use (defaults to the form's method, or POST if not defined)
9491 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9492 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9493 validate the form on the client (defaults to false)
9495 * @return {BasicForm} this
9497 doAction : function(action, options){
9498 if(typeof action == 'string'){
9499 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9501 if(this.fireEvent('beforeaction', this, action) !== false){
9502 this.beforeAction(action);
9503 action.run.defer(100, action);
9509 beforeAction : function(action){
9510 var o = action.options;
9515 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9517 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9520 // not really supported yet.. ??
9522 //if(this.waitMsgTarget === true){
9523 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9524 //}else if(this.waitMsgTarget){
9525 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9526 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9528 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9534 afterAction : function(action, success){
9535 this.activeAction = null;
9536 var o = action.options;
9541 Roo.get(document.body).unmask();
9547 //if(this.waitMsgTarget === true){
9548 // this.el.unmask();
9549 //}else if(this.waitMsgTarget){
9550 // this.waitMsgTarget.unmask();
9552 // Roo.MessageBox.updateProgress(1);
9553 // Roo.MessageBox.hide();
9560 Roo.callback(o.success, o.scope, [this, action]);
9561 this.fireEvent('actioncomplete', this, action);
9565 // failure condition..
9566 // we have a scenario where updates need confirming.
9567 // eg. if a locking scenario exists..
9568 // we look for { errors : { needs_confirm : true }} in the response.
9570 (typeof(action.result) != 'undefined') &&
9571 (typeof(action.result.errors) != 'undefined') &&
9572 (typeof(action.result.errors.needs_confirm) != 'undefined')
9575 Roo.log("not supported yet");
9578 Roo.MessageBox.confirm(
9579 "Change requires confirmation",
9580 action.result.errorMsg,
9585 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9595 Roo.callback(o.failure, o.scope, [this, action]);
9596 // show an error message if no failed handler is set..
9597 if (!this.hasListener('actionfailed')) {
9598 Roo.log("need to add dialog support");
9600 Roo.MessageBox.alert("Error",
9601 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9602 action.result.errorMsg :
9603 "Saving Failed, please check your entries or try again"
9608 this.fireEvent('actionfailed', this, action);
9613 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9614 * @param {String} id The value to search for
9617 findField : function(id){
9618 var items = this.getItems();
9619 var field = items.get(id);
9621 items.each(function(f){
9622 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9629 return field || null;
9632 * Mark fields in this form invalid in bulk.
9633 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9634 * @return {BasicForm} this
9636 markInvalid : function(errors){
9637 if(errors instanceof Array){
9638 for(var i = 0, len = errors.length; i < len; i++){
9639 var fieldError = errors[i];
9640 var f = this.findField(fieldError.id);
9642 f.markInvalid(fieldError.msg);
9648 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9649 field.markInvalid(errors[id]);
9653 //Roo.each(this.childForms || [], function (f) {
9654 // f.markInvalid(errors);
9661 * Set values for fields in this form in bulk.
9662 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9663 * @return {BasicForm} this
9665 setValues : function(values){
9666 if(values instanceof Array){ // array of objects
9667 for(var i = 0, len = values.length; i < len; i++){
9669 var f = this.findField(v.id);
9671 f.setValue(v.value);
9672 if(this.trackResetOnLoad){
9673 f.originalValue = f.getValue();
9677 }else{ // object hash
9680 if(typeof values[id] != 'function' && (field = this.findField(id))){
9682 if (field.setFromData &&
9684 field.displayField &&
9685 // combos' with local stores can
9686 // be queried via setValue()
9687 // to set their value..
9688 (field.store && !field.store.isLocal)
9692 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9693 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9694 field.setFromData(sd);
9696 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9698 field.setFromData(values);
9701 field.setValue(values[id]);
9705 if(this.trackResetOnLoad){
9706 field.originalValue = field.getValue();
9712 //Roo.each(this.childForms || [], function (f) {
9713 // f.setValues(values);
9720 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9721 * they are returned as an array.
9722 * @param {Boolean} asString
9725 getValues : function(asString){
9726 //if (this.childForms) {
9727 // copy values from the child forms
9728 // Roo.each(this.childForms, function (f) {
9729 // this.setValues(f.getValues());
9735 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9736 if(asString === true){
9739 return Roo.urlDecode(fs);
9743 * Returns the fields in this form as an object with key/value pairs.
9744 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9747 getFieldValues : function(with_hidden)
9749 var items = this.getItems();
9751 items.each(function(f){
9757 var v = f.getValue();
9759 if (f.inputType =='radio') {
9760 if (typeof(ret[f.getName()]) == 'undefined') {
9761 ret[f.getName()] = ''; // empty..
9764 if (!f.el.dom.checked) {
9772 if(f.xtype == 'MoneyField'){
9773 ret[f.currencyName] = f.getCurrency();
9776 // not sure if this supported any more..
9777 if ((typeof(v) == 'object') && f.getRawValue) {
9778 v = f.getRawValue() ; // dates..
9780 // combo boxes where name != hiddenName...
9781 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9782 ret[f.name] = f.getRawValue();
9784 ret[f.getName()] = v;
9791 * Clears all invalid messages in this form.
9792 * @return {BasicForm} this
9794 clearInvalid : function(){
9795 var items = this.getItems();
9797 items.each(function(f){
9806 * @return {BasicForm} this
9809 var items = this.getItems();
9810 items.each(function(f){
9814 Roo.each(this.childForms || [], function (f) {
9822 getItems : function()
9824 var r=new Roo.util.MixedCollection(false, function(o){
9825 return o.id || (o.id = Roo.id());
9827 var iter = function(el) {
9834 Roo.each(el.items,function(e) {
9843 hideFields : function(items)
9845 Roo.each(items, function(i){
9847 var f = this.findField(i);
9858 showFields : function(items)
9860 Roo.each(items, function(i){
9862 var f = this.findField(i);
9875 Roo.apply(Roo.bootstrap.Form, {
9902 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9903 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9904 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9905 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9908 this.maskEl.top.enableDisplayMode("block");
9909 this.maskEl.left.enableDisplayMode("block");
9910 this.maskEl.bottom.enableDisplayMode("block");
9911 this.maskEl.right.enableDisplayMode("block");
9913 this.toolTip = new Roo.bootstrap.Tooltip({
9914 cls : 'roo-form-error-popover',
9916 'left' : ['r-l', [-2,0], 'right'],
9917 'right' : ['l-r', [2,0], 'left'],
9918 'bottom' : ['tl-bl', [0,2], 'top'],
9919 'top' : [ 'bl-tl', [0,-2], 'bottom']
9923 this.toolTip.render(Roo.get(document.body));
9925 this.toolTip.el.enableDisplayMode("block");
9927 Roo.get(document.body).on('click', function(){
9931 Roo.get(document.body).on('touchstart', function(){
9935 this.isApplied = true
9938 mask : function(form, target)
9942 this.target = target;
9944 if(!this.form.errorMask || !target.el){
9948 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9950 Roo.log(scrollable);
9952 var ot = this.target.el.calcOffsetsTo(scrollable);
9954 var scrollTo = ot[1] - this.form.maskOffset;
9956 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9958 scrollable.scrollTo('top', scrollTo);
9960 var box = this.target.el.getBox();
9962 var zIndex = Roo.bootstrap.Modal.zIndex++;
9965 this.maskEl.top.setStyle('position', 'absolute');
9966 this.maskEl.top.setStyle('z-index', zIndex);
9967 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9968 this.maskEl.top.setLeft(0);
9969 this.maskEl.top.setTop(0);
9970 this.maskEl.top.show();
9972 this.maskEl.left.setStyle('position', 'absolute');
9973 this.maskEl.left.setStyle('z-index', zIndex);
9974 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9975 this.maskEl.left.setLeft(0);
9976 this.maskEl.left.setTop(box.y - this.padding);
9977 this.maskEl.left.show();
9979 this.maskEl.bottom.setStyle('position', 'absolute');
9980 this.maskEl.bottom.setStyle('z-index', zIndex);
9981 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9982 this.maskEl.bottom.setLeft(0);
9983 this.maskEl.bottom.setTop(box.bottom + this.padding);
9984 this.maskEl.bottom.show();
9986 this.maskEl.right.setStyle('position', 'absolute');
9987 this.maskEl.right.setStyle('z-index', zIndex);
9988 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9989 this.maskEl.right.setLeft(box.right + this.padding);
9990 this.maskEl.right.setTop(box.y - this.padding);
9991 this.maskEl.right.show();
9993 this.toolTip.bindEl = this.target.el;
9995 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9997 var tip = this.target.blankText;
9999 if(this.target.getValue() !== '' ) {
10001 if (this.target.invalidText.length) {
10002 tip = this.target.invalidText;
10003 } else if (this.target.regexText.length){
10004 tip = this.target.regexText;
10008 this.toolTip.show(tip);
10010 this.intervalID = window.setInterval(function() {
10011 Roo.bootstrap.Form.popover.unmask();
10014 window.onwheel = function(){ return false;};
10016 (function(){ this.isMasked = true; }).defer(500, this);
10020 unmask : function()
10022 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10026 this.maskEl.top.setStyle('position', 'absolute');
10027 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10028 this.maskEl.top.hide();
10030 this.maskEl.left.setStyle('position', 'absolute');
10031 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10032 this.maskEl.left.hide();
10034 this.maskEl.bottom.setStyle('position', 'absolute');
10035 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10036 this.maskEl.bottom.hide();
10038 this.maskEl.right.setStyle('position', 'absolute');
10039 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10040 this.maskEl.right.hide();
10042 this.toolTip.hide();
10044 this.toolTip.el.hide();
10046 window.onwheel = function(){ return true;};
10048 if(this.intervalID){
10049 window.clearInterval(this.intervalID);
10050 this.intervalID = false;
10053 this.isMasked = false;
10063 * Ext JS Library 1.1.1
10064 * Copyright(c) 2006-2007, Ext JS, LLC.
10066 * Originally Released Under LGPL - original licence link has changed is not relivant.
10069 * <script type="text/javascript">
10072 * @class Roo.form.VTypes
10073 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10076 Roo.form.VTypes = function(){
10077 // closure these in so they are only created once.
10078 var alpha = /^[a-zA-Z_]+$/;
10079 var alphanum = /^[a-zA-Z0-9_]+$/;
10080 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10081 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10083 // All these messages and functions are configurable
10086 * The function used to validate email addresses
10087 * @param {String} value The email address
10089 'email' : function(v){
10090 return email.test(v);
10093 * The error text to display when the email validation function returns false
10096 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10098 * The keystroke filter mask to be applied on email input
10101 'emailMask' : /[a-z0-9_\.\-@]/i,
10104 * The function used to validate URLs
10105 * @param {String} value The URL
10107 'url' : function(v){
10108 return url.test(v);
10111 * The error text to display when the url validation function returns false
10114 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10117 * The function used to validate alpha values
10118 * @param {String} value The value
10120 'alpha' : function(v){
10121 return alpha.test(v);
10124 * The error text to display when the alpha validation function returns false
10127 'alphaText' : 'This field should only contain letters and _',
10129 * The keystroke filter mask to be applied on alpha input
10132 'alphaMask' : /[a-z_]/i,
10135 * The function used to validate alphanumeric values
10136 * @param {String} value The value
10138 'alphanum' : function(v){
10139 return alphanum.test(v);
10142 * The error text to display when the alphanumeric validation function returns false
10145 'alphanumText' : 'This field should only contain letters, numbers and _',
10147 * The keystroke filter mask to be applied on alphanumeric input
10150 'alphanumMask' : /[a-z0-9_]/i
10160 * @class Roo.bootstrap.Input
10161 * @extends Roo.bootstrap.Component
10162 * Bootstrap Input class
10163 * @cfg {Boolean} disabled is it disabled
10164 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10165 * @cfg {String} name name of the input
10166 * @cfg {string} fieldLabel - the label associated
10167 * @cfg {string} placeholder - placeholder to put in text.
10168 * @cfg {string} before - input group add on before
10169 * @cfg {string} after - input group add on after
10170 * @cfg {string} size - (lg|sm) or leave empty..
10171 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10172 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10173 * @cfg {Number} md colspan out of 12 for computer-sized screens
10174 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10175 * @cfg {string} value default value of the input
10176 * @cfg {Number} labelWidth set the width of label
10177 * @cfg {Number} labellg set the width of label (1-12)
10178 * @cfg {Number} labelmd set the width of label (1-12)
10179 * @cfg {Number} labelsm set the width of label (1-12)
10180 * @cfg {Number} labelxs set the width of label (1-12)
10181 * @cfg {String} labelAlign (top|left)
10182 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10183 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10184 * @cfg {String} indicatorpos (left|right) default left
10185 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10186 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10187 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10189 * @cfg {String} align (left|center|right) Default left
10190 * @cfg {Boolean} forceFeedback (true|false) Default false
10193 * Create a new Input
10194 * @param {Object} config The config object
10197 Roo.bootstrap.Input = function(config){
10199 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10204 * Fires when this field receives input focus.
10205 * @param {Roo.form.Field} this
10210 * Fires when this field loses input focus.
10211 * @param {Roo.form.Field} this
10215 * @event specialkey
10216 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10217 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10218 * @param {Roo.form.Field} this
10219 * @param {Roo.EventObject} e The event object
10224 * Fires just before the field blurs if the field value has changed.
10225 * @param {Roo.form.Field} this
10226 * @param {Mixed} newValue The new value
10227 * @param {Mixed} oldValue The original value
10232 * Fires after the field has been marked as invalid.
10233 * @param {Roo.form.Field} this
10234 * @param {String} msg The validation message
10239 * Fires after the field has been validated with no errors.
10240 * @param {Roo.form.Field} this
10245 * Fires after the key up
10246 * @param {Roo.form.Field} this
10247 * @param {Roo.EventObject} e The event Object
10253 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10255 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10256 automatic validation (defaults to "keyup").
10258 validationEvent : "keyup",
10260 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10262 validateOnBlur : true,
10264 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10266 validationDelay : 250,
10268 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10270 focusClass : "x-form-focus", // not needed???
10274 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10276 invalidClass : "has-warning",
10279 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10281 validClass : "has-success",
10284 * @cfg {Boolean} hasFeedback (true|false) default true
10286 hasFeedback : true,
10289 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10291 invalidFeedbackClass : "glyphicon-warning-sign",
10294 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10296 validFeedbackClass : "glyphicon-ok",
10299 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10301 selectOnFocus : false,
10304 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10308 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10313 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10315 disableKeyFilter : false,
10318 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10322 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10326 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10328 blankText : "Please complete this mandatory field",
10331 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10335 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10337 maxLength : Number.MAX_VALUE,
10339 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10341 minLengthText : "The minimum length for this field is {0}",
10343 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10345 maxLengthText : "The maximum length for this field is {0}",
10349 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10350 * If available, this function will be called only after the basic validators all return true, and will be passed the
10351 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10355 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10356 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10357 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10361 * @cfg {String} regexText -- Depricated - use Invalid Text
10366 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10372 autocomplete: false,
10376 inputType : 'text',
10379 placeholder: false,
10384 preventMark: false,
10385 isFormField : true,
10388 labelAlign : false,
10391 formatedValue : false,
10392 forceFeedback : false,
10394 indicatorpos : 'left',
10404 parentLabelAlign : function()
10407 while (parent.parent()) {
10408 parent = parent.parent();
10409 if (typeof(parent.labelAlign) !='undefined') {
10410 return parent.labelAlign;
10417 getAutoCreate : function()
10419 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10425 if(this.inputType != 'hidden'){
10426 cfg.cls = 'form-group' //input-group
10432 type : this.inputType,
10433 value : this.value,
10434 cls : 'form-control',
10435 placeholder : this.placeholder || '',
10436 autocomplete : this.autocomplete || 'new-password'
10439 if(this.capture.length){
10440 input.capture = this.capture;
10443 if(this.accept.length){
10444 input.accept = this.accept + "/*";
10448 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10451 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10452 input.maxLength = this.maxLength;
10455 if (this.disabled) {
10456 input.disabled=true;
10459 if (this.readOnly) {
10460 input.readonly=true;
10464 input.name = this.name;
10468 input.cls += ' input-' + this.size;
10472 ['xs','sm','md','lg'].map(function(size){
10473 if (settings[size]) {
10474 cfg.cls += ' col-' + size + '-' + settings[size];
10478 var inputblock = input;
10482 cls: 'glyphicon form-control-feedback'
10485 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10488 cls : 'has-feedback',
10496 if (this.before || this.after) {
10499 cls : 'input-group',
10503 if (this.before && typeof(this.before) == 'string') {
10505 inputblock.cn.push({
10507 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10511 if (this.before && typeof(this.before) == 'object') {
10512 this.before = Roo.factory(this.before);
10514 inputblock.cn.push({
10516 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10517 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10521 inputblock.cn.push(input);
10523 if (this.after && typeof(this.after) == 'string') {
10524 inputblock.cn.push({
10526 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10530 if (this.after && typeof(this.after) == 'object') {
10531 this.after = Roo.factory(this.after);
10533 inputblock.cn.push({
10535 cls : 'roo-input-after input-group-append input-group-text input-group-' +
10536 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10540 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10541 inputblock.cls += ' has-feedback';
10542 inputblock.cn.push(feedback);
10547 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10548 tooltip : 'This field is required'
10550 if (Roo.bootstrap.version == 4) {
10553 style : 'display-none'
10556 if (align ==='left' && this.fieldLabel.length) {
10558 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10565 cls : 'control-label col-form-label',
10566 html : this.fieldLabel
10577 var labelCfg = cfg.cn[1];
10578 var contentCfg = cfg.cn[2];
10580 if(this.indicatorpos == 'right'){
10585 cls : 'control-label col-form-label',
10589 html : this.fieldLabel
10603 labelCfg = cfg.cn[0];
10604 contentCfg = cfg.cn[1];
10608 if(this.labelWidth > 12){
10609 labelCfg.style = "width: " + this.labelWidth + 'px';
10612 if(this.labelWidth < 13 && this.labelmd == 0){
10613 this.labelmd = this.labelWidth;
10616 if(this.labellg > 0){
10617 labelCfg.cls += ' col-lg-' + this.labellg;
10618 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10621 if(this.labelmd > 0){
10622 labelCfg.cls += ' col-md-' + this.labelmd;
10623 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10626 if(this.labelsm > 0){
10627 labelCfg.cls += ' col-sm-' + this.labelsm;
10628 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10631 if(this.labelxs > 0){
10632 labelCfg.cls += ' col-xs-' + this.labelxs;
10633 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10637 } else if ( this.fieldLabel.length) {
10642 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10643 tooltip : 'This field is required'
10647 //cls : 'input-group-addon',
10648 html : this.fieldLabel
10656 if(this.indicatorpos == 'right'){
10661 //cls : 'input-group-addon',
10662 html : this.fieldLabel
10667 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10668 tooltip : 'This field is required'
10688 if (this.parentType === 'Navbar' && this.parent().bar) {
10689 cfg.cls += ' navbar-form';
10692 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10693 // on BS4 we do this only if not form
10694 cfg.cls += ' navbar-form';
10702 * return the real input element.
10704 inputEl: function ()
10706 return this.el.select('input.form-control',true).first();
10709 tooltipEl : function()
10711 return this.inputEl();
10714 indicatorEl : function()
10716 if (Roo.bootstrap.version == 4) {
10717 return false; // not enabled in v4 yet.
10720 var indicator = this.el.select('i.roo-required-indicator',true).first();
10730 setDisabled : function(v)
10732 var i = this.inputEl().dom;
10734 i.removeAttribute('disabled');
10738 i.setAttribute('disabled','true');
10740 initEvents : function()
10743 this.inputEl().on("keydown" , this.fireKey, this);
10744 this.inputEl().on("focus", this.onFocus, this);
10745 this.inputEl().on("blur", this.onBlur, this);
10747 this.inputEl().relayEvent('keyup', this);
10749 this.indicator = this.indicatorEl();
10751 if(this.indicator){
10752 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10755 // reference to original value for reset
10756 this.originalValue = this.getValue();
10757 //Roo.form.TextField.superclass.initEvents.call(this);
10758 if(this.validationEvent == 'keyup'){
10759 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10760 this.inputEl().on('keyup', this.filterValidation, this);
10762 else if(this.validationEvent !== false){
10763 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10766 if(this.selectOnFocus){
10767 this.on("focus", this.preFocus, this);
10770 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10771 this.inputEl().on("keypress", this.filterKeys, this);
10773 this.inputEl().relayEvent('keypress', this);
10776 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10777 this.el.on("click", this.autoSize, this);
10780 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10781 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10784 if (typeof(this.before) == 'object') {
10785 this.before.render(this.el.select('.roo-input-before',true).first());
10787 if (typeof(this.after) == 'object') {
10788 this.after.render(this.el.select('.roo-input-after',true).first());
10791 this.inputEl().on('change', this.onChange, this);
10794 filterValidation : function(e){
10795 if(!e.isNavKeyPress()){
10796 this.validationTask.delay(this.validationDelay);
10800 * Validates the field value
10801 * @return {Boolean} True if the value is valid, else false
10803 validate : function(){
10804 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10805 if(this.disabled || this.validateValue(this.getRawValue())){
10810 this.markInvalid();
10816 * Validates a value according to the field's validation rules and marks the field as invalid
10817 * if the validation fails
10818 * @param {Mixed} value The value to validate
10819 * @return {Boolean} True if the value is valid, else false
10821 validateValue : function(value)
10823 if(this.getVisibilityEl().hasClass('hidden')){
10827 if(value.length < 1) { // if it's blank
10828 if(this.allowBlank){
10834 if(value.length < this.minLength){
10837 if(value.length > this.maxLength){
10841 var vt = Roo.form.VTypes;
10842 if(!vt[this.vtype](value, this)){
10846 if(typeof this.validator == "function"){
10847 var msg = this.validator(value);
10851 if (typeof(msg) == 'string') {
10852 this.invalidText = msg;
10856 if(this.regex && !this.regex.test(value)){
10864 fireKey : function(e){
10865 //Roo.log('field ' + e.getKey());
10866 if(e.isNavKeyPress()){
10867 this.fireEvent("specialkey", this, e);
10870 focus : function (selectText){
10872 this.inputEl().focus();
10873 if(selectText === true){
10874 this.inputEl().dom.select();
10880 onFocus : function(){
10881 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10882 // this.el.addClass(this.focusClass);
10884 if(!this.hasFocus){
10885 this.hasFocus = true;
10886 this.startValue = this.getValue();
10887 this.fireEvent("focus", this);
10891 beforeBlur : Roo.emptyFn,
10895 onBlur : function(){
10897 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10898 //this.el.removeClass(this.focusClass);
10900 this.hasFocus = false;
10901 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10904 var v = this.getValue();
10905 if(String(v) !== String(this.startValue)){
10906 this.fireEvent('change', this, v, this.startValue);
10908 this.fireEvent("blur", this);
10911 onChange : function(e)
10913 var v = this.getValue();
10914 if(String(v) !== String(this.startValue)){
10915 this.fireEvent('change', this, v, this.startValue);
10921 * Resets the current field value to the originally loaded value and clears any validation messages
10923 reset : function(){
10924 this.setValue(this.originalValue);
10928 * Returns the name of the field
10929 * @return {Mixed} name The name field
10931 getName: function(){
10935 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10936 * @return {Mixed} value The field value
10938 getValue : function(){
10940 var v = this.inputEl().getValue();
10945 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10946 * @return {Mixed} value The field value
10948 getRawValue : function(){
10949 var v = this.inputEl().getValue();
10955 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10956 * @param {Mixed} value The value to set
10958 setRawValue : function(v){
10959 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10962 selectText : function(start, end){
10963 var v = this.getRawValue();
10965 start = start === undefined ? 0 : start;
10966 end = end === undefined ? v.length : end;
10967 var d = this.inputEl().dom;
10968 if(d.setSelectionRange){
10969 d.setSelectionRange(start, end);
10970 }else if(d.createTextRange){
10971 var range = d.createTextRange();
10972 range.moveStart("character", start);
10973 range.moveEnd("character", v.length-end);
10980 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10981 * @param {Mixed} value The value to set
10983 setValue : function(v){
10986 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10992 processValue : function(value){
10993 if(this.stripCharsRe){
10994 var newValue = value.replace(this.stripCharsRe, '');
10995 if(newValue !== value){
10996 this.setRawValue(newValue);
11003 preFocus : function(){
11005 if(this.selectOnFocus){
11006 this.inputEl().dom.select();
11009 filterKeys : function(e){
11010 var k = e.getKey();
11011 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11014 var c = e.getCharCode(), cc = String.fromCharCode(c);
11015 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11018 if(!this.maskRe.test(cc)){
11023 * Clear any invalid styles/messages for this field
11025 clearInvalid : function(){
11027 if(!this.el || this.preventMark){ // not rendered
11032 this.el.removeClass([this.invalidClass, 'is-invalid']);
11034 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11036 var feedback = this.el.select('.form-control-feedback', true).first();
11039 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11044 if(this.indicator){
11045 this.indicator.removeClass('visible');
11046 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11049 this.fireEvent('valid', this);
11053 * Mark this field as valid
11055 markValid : function()
11057 if(!this.el || this.preventMark){ // not rendered...
11061 this.el.removeClass([this.invalidClass, this.validClass]);
11062 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11064 var feedback = this.el.select('.form-control-feedback', true).first();
11067 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11070 if(this.indicator){
11071 this.indicator.removeClass('visible');
11072 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11080 if(this.allowBlank && !this.getRawValue().length){
11083 if (Roo.bootstrap.version == 3) {
11084 this.el.addClass(this.validClass);
11086 this.inputEl().addClass('is-valid');
11089 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11091 var feedback = this.el.select('.form-control-feedback', true).first();
11094 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11095 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11100 this.fireEvent('valid', this);
11104 * Mark this field as invalid
11105 * @param {String} msg The validation message
11107 markInvalid : function(msg)
11109 if(!this.el || this.preventMark){ // not rendered
11113 this.el.removeClass([this.invalidClass, this.validClass]);
11114 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11116 var feedback = this.el.select('.form-control-feedback', true).first();
11119 this.el.select('.form-control-feedback', true).first().removeClass(
11120 [this.invalidFeedbackClass, this.validFeedbackClass]);
11127 if(this.allowBlank && !this.getRawValue().length){
11131 if(this.indicator){
11132 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11133 this.indicator.addClass('visible');
11135 if (Roo.bootstrap.version == 3) {
11136 this.el.addClass(this.invalidClass);
11138 this.inputEl().addClass('is-invalid');
11143 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11145 var feedback = this.el.select('.form-control-feedback', true).first();
11148 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11150 if(this.getValue().length || this.forceFeedback){
11151 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11158 this.fireEvent('invalid', this, msg);
11161 SafariOnKeyDown : function(event)
11163 // this is a workaround for a password hang bug on chrome/ webkit.
11164 if (this.inputEl().dom.type != 'password') {
11168 var isSelectAll = false;
11170 if(this.inputEl().dom.selectionEnd > 0){
11171 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11173 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11174 event.preventDefault();
11179 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11181 event.preventDefault();
11182 // this is very hacky as keydown always get's upper case.
11184 var cc = String.fromCharCode(event.getCharCode());
11185 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11189 adjustWidth : function(tag, w){
11190 tag = tag.toLowerCase();
11191 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11192 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11193 if(tag == 'input'){
11196 if(tag == 'textarea'){
11199 }else if(Roo.isOpera){
11200 if(tag == 'input'){
11203 if(tag == 'textarea'){
11211 setFieldLabel : function(v)
11213 if(!this.rendered){
11217 if(this.indicatorEl()){
11218 var ar = this.el.select('label > span',true);
11220 if (ar.elements.length) {
11221 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11222 this.fieldLabel = v;
11226 var br = this.el.select('label',true);
11228 if(br.elements.length) {
11229 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11230 this.fieldLabel = v;
11234 Roo.log('Cannot Found any of label > span || label in input');
11238 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11239 this.fieldLabel = v;
11254 * @class Roo.bootstrap.TextArea
11255 * @extends Roo.bootstrap.Input
11256 * Bootstrap TextArea class
11257 * @cfg {Number} cols Specifies the visible width of a text area
11258 * @cfg {Number} rows Specifies the visible number of lines in a text area
11259 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11260 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11261 * @cfg {string} html text
11264 * Create a new TextArea
11265 * @param {Object} config The config object
11268 Roo.bootstrap.TextArea = function(config){
11269 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11273 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11283 getAutoCreate : function(){
11285 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11291 if(this.inputType != 'hidden'){
11292 cfg.cls = 'form-group' //input-group
11300 value : this.value || '',
11301 html: this.html || '',
11302 cls : 'form-control',
11303 placeholder : this.placeholder || ''
11307 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11308 input.maxLength = this.maxLength;
11312 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11316 input.cols = this.cols;
11319 if (this.readOnly) {
11320 input.readonly = true;
11324 input.name = this.name;
11328 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11332 ['xs','sm','md','lg'].map(function(size){
11333 if (settings[size]) {
11334 cfg.cls += ' col-' + size + '-' + settings[size];
11338 var inputblock = input;
11340 if(this.hasFeedback && !this.allowBlank){
11344 cls: 'glyphicon form-control-feedback'
11348 cls : 'has-feedback',
11357 if (this.before || this.after) {
11360 cls : 'input-group',
11364 inputblock.cn.push({
11366 cls : 'input-group-addon',
11371 inputblock.cn.push(input);
11373 if(this.hasFeedback && !this.allowBlank){
11374 inputblock.cls += ' has-feedback';
11375 inputblock.cn.push(feedback);
11379 inputblock.cn.push({
11381 cls : 'input-group-addon',
11388 if (align ==='left' && this.fieldLabel.length) {
11393 cls : 'control-label',
11394 html : this.fieldLabel
11405 if(this.labelWidth > 12){
11406 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11409 if(this.labelWidth < 13 && this.labelmd == 0){
11410 this.labelmd = this.labelWidth;
11413 if(this.labellg > 0){
11414 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11415 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11418 if(this.labelmd > 0){
11419 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11420 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11423 if(this.labelsm > 0){
11424 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11425 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11428 if(this.labelxs > 0){
11429 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11430 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11433 } else if ( this.fieldLabel.length) {
11438 //cls : 'input-group-addon',
11439 html : this.fieldLabel
11457 if (this.disabled) {
11458 input.disabled=true;
11465 * return the real textarea element.
11467 inputEl: function ()
11469 return this.el.select('textarea.form-control',true).first();
11473 * Clear any invalid styles/messages for this field
11475 clearInvalid : function()
11478 if(!this.el || this.preventMark){ // not rendered
11482 var label = this.el.select('label', true).first();
11483 var icon = this.el.select('i.fa-star', true).first();
11488 this.el.removeClass( this.validClass);
11489 this.inputEl().removeClass('is-invalid');
11491 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11493 var feedback = this.el.select('.form-control-feedback', true).first();
11496 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11501 this.fireEvent('valid', this);
11505 * Mark this field as valid
11507 markValid : function()
11509 if(!this.el || this.preventMark){ // not rendered
11513 this.el.removeClass([this.invalidClass, this.validClass]);
11514 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11516 var feedback = this.el.select('.form-control-feedback', true).first();
11519 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11522 if(this.disabled || this.allowBlank){
11526 var label = this.el.select('label', true).first();
11527 var icon = this.el.select('i.fa-star', true).first();
11532 if (Roo.bootstrap.version == 3) {
11533 this.el.addClass(this.validClass);
11535 this.inputEl().addClass('is-valid');
11539 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11541 var feedback = this.el.select('.form-control-feedback', true).first();
11544 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11545 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11550 this.fireEvent('valid', this);
11554 * Mark this field as invalid
11555 * @param {String} msg The validation message
11557 markInvalid : function(msg)
11559 if(!this.el || this.preventMark){ // not rendered
11563 this.el.removeClass([this.invalidClass, this.validClass]);
11564 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11566 var feedback = this.el.select('.form-control-feedback', true).first();
11569 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11572 if(this.disabled || this.allowBlank){
11576 var label = this.el.select('label', true).first();
11577 var icon = this.el.select('i.fa-star', true).first();
11579 if(!this.getValue().length && label && !icon){
11580 this.el.createChild({
11582 cls : 'text-danger fa fa-lg fa-star',
11583 tooltip : 'This field is required',
11584 style : 'margin-right:5px;'
11588 if (Roo.bootstrap.version == 3) {
11589 this.el.addClass(this.invalidClass);
11591 this.inputEl().addClass('is-invalid');
11594 // fixme ... this may be depricated need to test..
11595 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11597 var feedback = this.el.select('.form-control-feedback', true).first();
11600 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11602 if(this.getValue().length || this.forceFeedback){
11603 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11610 this.fireEvent('invalid', this, msg);
11618 * trigger field - base class for combo..
11623 * @class Roo.bootstrap.TriggerField
11624 * @extends Roo.bootstrap.Input
11625 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11626 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11627 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11628 * for which you can provide a custom implementation. For example:
11630 var trigger = new Roo.bootstrap.TriggerField();
11631 trigger.onTriggerClick = myTriggerFn;
11632 trigger.applyTo('my-field');
11635 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11636 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11637 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11638 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11639 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11642 * Create a new TriggerField.
11643 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11644 * to the base TextField)
11646 Roo.bootstrap.TriggerField = function(config){
11647 this.mimicing = false;
11648 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11651 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11653 * @cfg {String} triggerClass A CSS class to apply to the trigger
11656 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11661 * @cfg {Boolean} removable (true|false) special filter default false
11665 /** @cfg {Boolean} grow @hide */
11666 /** @cfg {Number} growMin @hide */
11667 /** @cfg {Number} growMax @hide */
11673 autoSize: Roo.emptyFn,
11677 deferHeight : true,
11680 actionMode : 'wrap',
11685 getAutoCreate : function(){
11687 var align = this.labelAlign || this.parentLabelAlign();
11692 cls: 'form-group' //input-group
11699 type : this.inputType,
11700 cls : 'form-control',
11701 autocomplete: 'new-password',
11702 placeholder : this.placeholder || ''
11706 input.name = this.name;
11709 input.cls += ' input-' + this.size;
11712 if (this.disabled) {
11713 input.disabled=true;
11716 var inputblock = input;
11718 if(this.hasFeedback && !this.allowBlank){
11722 cls: 'glyphicon form-control-feedback'
11725 if(this.removable && !this.editable ){
11727 cls : 'has-feedback',
11733 cls : 'roo-combo-removable-btn close'
11740 cls : 'has-feedback',
11749 if(this.removable && !this.editable ){
11751 cls : 'roo-removable',
11757 cls : 'roo-combo-removable-btn close'
11764 if (this.before || this.after) {
11767 cls : 'input-group',
11771 inputblock.cn.push({
11773 cls : 'input-group-addon input-group-prepend input-group-text',
11778 inputblock.cn.push(input);
11780 if(this.hasFeedback && !this.allowBlank){
11781 inputblock.cls += ' has-feedback';
11782 inputblock.cn.push(feedback);
11786 inputblock.cn.push({
11788 cls : 'input-group-addon input-group-append input-group-text',
11797 var ibwrap = inputblock;
11802 cls: 'roo-select2-choices',
11806 cls: 'roo-select2-search-field',
11818 cls: 'roo-select2-container input-group',
11823 cls: 'form-hidden-field'
11829 if(!this.multiple && this.showToggleBtn){
11835 if (this.caret != false) {
11838 cls: 'fa fa-' + this.caret
11845 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11847 Roo.bootstrap.version == 3 ? caret : '',
11850 cls: 'combobox-clear',
11864 combobox.cls += ' roo-select2-container-multi';
11868 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11869 tooltip : 'This field is required'
11871 if (Roo.bootstrap.version == 4) {
11874 style : 'display:none'
11879 if (align ==='left' && this.fieldLabel.length) {
11881 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11888 cls : 'control-label',
11889 html : this.fieldLabel
11901 var labelCfg = cfg.cn[1];
11902 var contentCfg = cfg.cn[2];
11904 if(this.indicatorpos == 'right'){
11909 cls : 'control-label',
11913 html : this.fieldLabel
11927 labelCfg = cfg.cn[0];
11928 contentCfg = cfg.cn[1];
11931 if(this.labelWidth > 12){
11932 labelCfg.style = "width: " + this.labelWidth + 'px';
11935 if(this.labelWidth < 13 && this.labelmd == 0){
11936 this.labelmd = this.labelWidth;
11939 if(this.labellg > 0){
11940 labelCfg.cls += ' col-lg-' + this.labellg;
11941 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11944 if(this.labelmd > 0){
11945 labelCfg.cls += ' col-md-' + this.labelmd;
11946 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11949 if(this.labelsm > 0){
11950 labelCfg.cls += ' col-sm-' + this.labelsm;
11951 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11954 if(this.labelxs > 0){
11955 labelCfg.cls += ' col-xs-' + this.labelxs;
11956 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11959 } else if ( this.fieldLabel.length) {
11960 // Roo.log(" label");
11965 //cls : 'input-group-addon',
11966 html : this.fieldLabel
11974 if(this.indicatorpos == 'right'){
11982 html : this.fieldLabel
11996 // Roo.log(" no label && no align");
12003 ['xs','sm','md','lg'].map(function(size){
12004 if (settings[size]) {
12005 cfg.cls += ' col-' + size + '-' + settings[size];
12016 onResize : function(w, h){
12017 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12018 // if(typeof w == 'number'){
12019 // var x = w - this.trigger.getWidth();
12020 // this.inputEl().setWidth(this.adjustWidth('input', x));
12021 // this.trigger.setStyle('left', x+'px');
12026 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12029 getResizeEl : function(){
12030 return this.inputEl();
12034 getPositionEl : function(){
12035 return this.inputEl();
12039 alignErrorIcon : function(){
12040 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12044 initEvents : function(){
12048 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12049 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12050 if(!this.multiple && this.showToggleBtn){
12051 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12052 if(this.hideTrigger){
12053 this.trigger.setDisplayed(false);
12055 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12059 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12062 if(this.removable && !this.editable && !this.tickable){
12063 var close = this.closeTriggerEl();
12066 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12067 close.on('click', this.removeBtnClick, this, close);
12071 //this.trigger.addClassOnOver('x-form-trigger-over');
12072 //this.trigger.addClassOnClick('x-form-trigger-click');
12075 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12079 closeTriggerEl : function()
12081 var close = this.el.select('.roo-combo-removable-btn', true).first();
12082 return close ? close : false;
12085 removeBtnClick : function(e, h, el)
12087 e.preventDefault();
12089 if(this.fireEvent("remove", this) !== false){
12091 this.fireEvent("afterremove", this)
12095 createList : function()
12097 this.list = Roo.get(document.body).createChild({
12098 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12099 cls: 'typeahead typeahead-long dropdown-menu',
12100 style: 'display:none'
12103 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12108 initTrigger : function(){
12113 onDestroy : function(){
12115 this.trigger.removeAllListeners();
12116 // this.trigger.remove();
12119 // this.wrap.remove();
12121 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12125 onFocus : function(){
12126 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12128 if(!this.mimicing){
12129 this.wrap.addClass('x-trigger-wrap-focus');
12130 this.mimicing = true;
12131 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12132 if(this.monitorTab){
12133 this.el.on("keydown", this.checkTab, this);
12140 checkTab : function(e){
12141 if(e.getKey() == e.TAB){
12142 this.triggerBlur();
12147 onBlur : function(){
12152 mimicBlur : function(e, t){
12154 if(!this.wrap.contains(t) && this.validateBlur()){
12155 this.triggerBlur();
12161 triggerBlur : function(){
12162 this.mimicing = false;
12163 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12164 if(this.monitorTab){
12165 this.el.un("keydown", this.checkTab, this);
12167 //this.wrap.removeClass('x-trigger-wrap-focus');
12168 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12172 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12173 validateBlur : function(e, t){
12178 onDisable : function(){
12179 this.inputEl().dom.disabled = true;
12180 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12182 // this.wrap.addClass('x-item-disabled');
12187 onEnable : function(){
12188 this.inputEl().dom.disabled = false;
12189 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12191 // this.el.removeClass('x-item-disabled');
12196 onShow : function(){
12197 var ae = this.getActionEl();
12200 ae.dom.style.display = '';
12201 ae.dom.style.visibility = 'visible';
12207 onHide : function(){
12208 var ae = this.getActionEl();
12209 ae.dom.style.display = 'none';
12213 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12214 * by an implementing function.
12216 * @param {EventObject} e
12218 onTriggerClick : Roo.emptyFn
12226 * @class Roo.bootstrap.CardUploader
12227 * @extends Roo.bootstrap.Button
12228 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12229 * @cfg {Number} errorTimeout default 3000
12230 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12231 * @cfg {Array} html The button text.
12235 * Create a new CardUploader
12236 * @param {Object} config The config object
12239 Roo.bootstrap.CardUploader = function(config){
12243 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12246 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12253 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12256 errorTimeout : 3000,
12260 fileCollection : false,
12263 getAutoCreate : function()
12267 cls :'form-group' ,
12272 //cls : 'input-group-addon',
12273 html : this.fieldLabel
12280 value : this.value,
12281 cls : 'd-none form-control'
12286 multiple : 'multiple',
12288 cls : 'd-none roo-card-upload-selector'
12292 cls : 'roo-card-uploader-button-container w-100 mb-2'
12295 cls : 'card-columns roo-card-uploader-container'
12305 getChildContainer : function() /// what children are added to.
12307 return this.containerEl;
12310 getButtonContainer : function() /// what children are added to.
12312 return this.el.select(".roo-card-uploader-button-container").first();
12315 initEvents : function()
12318 Roo.bootstrap.Input.prototype.initEvents.call(this);
12322 xns: Roo.bootstrap,
12325 container_method : 'getButtonContainer' ,
12326 html : this.html, // fix changable?
12329 'click' : function(btn, e) {
12338 this.urlAPI = (window.createObjectURL && window) ||
12339 (window.URL && URL.revokeObjectURL && URL) ||
12340 (window.webkitURL && webkitURL);
12345 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12347 this.selectorEl.on('change', this.onFileSelected, this);
12350 this.images.forEach(function(img) {
12353 this.images = false;
12355 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12361 onClick : function(e)
12363 e.preventDefault();
12365 this.selectorEl.dom.click();
12369 onFileSelected : function(e)
12371 e.preventDefault();
12373 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12377 Roo.each(this.selectorEl.dom.files, function(file){
12378 this.addFile(file);
12387 addFile : function(file)
12390 if(typeof(file) === 'string'){
12391 throw "Add file by name?"; // should not happen
12395 if(!file || !this.urlAPI){
12405 var url = _this.urlAPI.createObjectURL( file);
12408 id : Roo.bootstrap.CardUploader.ID--,
12409 is_uploaded : false,
12412 mimetype : file.type,
12419 addCard : function (data)
12421 // hidden input element?
12422 // if the file is not an image...
12423 //then we need to use something other that and header_image
12428 xns : Roo.bootstrap,
12429 xtype : 'CardFooter',
12432 xns : Roo.bootstrap,
12438 xns : Roo.bootstrap,
12440 html : String.format("<small>{0}</small>", data.title),
12441 cls : 'col-11 text-left',
12446 click : function() {
12447 this.downloadCard(data.id)
12453 xns : Roo.bootstrap,
12461 click : function() {
12462 t.removeCard(data.id)
12474 var cn = this.addxtype(
12477 xns : Roo.bootstrap,
12480 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12481 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12482 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12487 initEvents : function() {
12488 Roo.bootstrap.Card.prototype.initEvents.call(this);
12489 this.imgEl = this.el.select('.card-img-top').first();
12491 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12492 this.imgEl.set({ 'pointer' : 'cursor' });
12501 // dont' really need ot update items.
12502 // this.items.push(cn);
12503 this.fileCollection.add(cn);
12504 this.updateInput();
12507 removeCard : function(id)
12510 var card = this.fileCollection.get(id);
12511 card.data.is_deleted = 1;
12512 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12513 this.fileCollection.remove(card);
12514 //this.items = this.items.filter(function(e) { return e != card });
12515 // dont' really need ot update items.
12516 card.el.dom.parentNode.removeChild(card.el.dom);
12521 this.fileCollection.each(function(card) {
12522 card.el.dom.parentNode.removeChild(card.el.dom);
12524 this.fileCollection.clear();
12525 this.updateInput();
12528 updateInput : function()
12531 this.fileCollection.each(function(e) {
12535 this.inputEl().dom.value = JSON.stringify(data);
12542 Roo.bootstrap.CardUploader.ID = -1;/*
12544 * Ext JS Library 1.1.1
12545 * Copyright(c) 2006-2007, Ext JS, LLC.
12547 * Originally Released Under LGPL - original licence link has changed is not relivant.
12550 * <script type="text/javascript">
12555 * @class Roo.data.SortTypes
12557 * Defines the default sorting (casting?) comparison functions used when sorting data.
12559 Roo.data.SortTypes = {
12561 * Default sort that does nothing
12562 * @param {Mixed} s The value being converted
12563 * @return {Mixed} The comparison value
12565 none : function(s){
12570 * The regular expression used to strip tags
12574 stripTagsRE : /<\/?[^>]+>/gi,
12577 * Strips all HTML tags to sort on text only
12578 * @param {Mixed} s The value being converted
12579 * @return {String} The comparison value
12581 asText : function(s){
12582 return String(s).replace(this.stripTagsRE, "");
12586 * Strips all HTML tags to sort on text only - Case insensitive
12587 * @param {Mixed} s The value being converted
12588 * @return {String} The comparison value
12590 asUCText : function(s){
12591 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12595 * Case insensitive string
12596 * @param {Mixed} s The value being converted
12597 * @return {String} The comparison value
12599 asUCString : function(s) {
12600 return String(s).toUpperCase();
12605 * @param {Mixed} s The value being converted
12606 * @return {Number} The comparison value
12608 asDate : function(s) {
12612 if(s instanceof Date){
12613 return s.getTime();
12615 return Date.parse(String(s));
12620 * @param {Mixed} s The value being converted
12621 * @return {Float} The comparison value
12623 asFloat : function(s) {
12624 var val = parseFloat(String(s).replace(/,/g, ""));
12633 * @param {Mixed} s The value being converted
12634 * @return {Number} The comparison value
12636 asInt : function(s) {
12637 var val = parseInt(String(s).replace(/,/g, ""));
12645 * Ext JS Library 1.1.1
12646 * Copyright(c) 2006-2007, Ext JS, LLC.
12648 * Originally Released Under LGPL - original licence link has changed is not relivant.
12651 * <script type="text/javascript">
12655 * @class Roo.data.Record
12656 * Instances of this class encapsulate both record <em>definition</em> information, and record
12657 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12658 * to access Records cached in an {@link Roo.data.Store} object.<br>
12660 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12661 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12664 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12666 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12667 * {@link #create}. The parameters are the same.
12668 * @param {Array} data An associative Array of data values keyed by the field name.
12669 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12670 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12671 * not specified an integer id is generated.
12673 Roo.data.Record = function(data, id){
12674 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12679 * Generate a constructor for a specific record layout.
12680 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12681 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12682 * Each field definition object may contain the following properties: <ul>
12683 * <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,
12684 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12685 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12686 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12687 * is being used, then this is a string containing the javascript expression to reference the data relative to
12688 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12689 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12690 * this may be omitted.</p></li>
12691 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12692 * <ul><li>auto (Default, implies no conversion)</li>
12697 * <li>date</li></ul></p></li>
12698 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12699 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12700 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12701 * by the Reader into an object that will be stored in the Record. It is passed the
12702 * following parameters:<ul>
12703 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12705 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12707 * <br>usage:<br><pre><code>
12708 var TopicRecord = Roo.data.Record.create(
12709 {name: 'title', mapping: 'topic_title'},
12710 {name: 'author', mapping: 'username'},
12711 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12712 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12713 {name: 'lastPoster', mapping: 'user2'},
12714 {name: 'excerpt', mapping: 'post_text'}
12717 var myNewRecord = new TopicRecord({
12718 title: 'Do my job please',
12721 lastPost: new Date(),
12722 lastPoster: 'Animal',
12723 excerpt: 'No way dude!'
12725 myStore.add(myNewRecord);
12730 Roo.data.Record.create = function(o){
12731 var f = function(){
12732 f.superclass.constructor.apply(this, arguments);
12734 Roo.extend(f, Roo.data.Record);
12735 var p = f.prototype;
12736 p.fields = new Roo.util.MixedCollection(false, function(field){
12739 for(var i = 0, len = o.length; i < len; i++){
12740 p.fields.add(new Roo.data.Field(o[i]));
12742 f.getField = function(name){
12743 return p.fields.get(name);
12748 Roo.data.Record.AUTO_ID = 1000;
12749 Roo.data.Record.EDIT = 'edit';
12750 Roo.data.Record.REJECT = 'reject';
12751 Roo.data.Record.COMMIT = 'commit';
12753 Roo.data.Record.prototype = {
12755 * Readonly flag - true if this record has been modified.
12764 join : function(store){
12765 this.store = store;
12769 * Set the named field to the specified value.
12770 * @param {String} name The name of the field to set.
12771 * @param {Object} value The value to set the field to.
12773 set : function(name, value){
12774 if(this.data[name] == value){
12778 if(!this.modified){
12779 this.modified = {};
12781 if(typeof this.modified[name] == 'undefined'){
12782 this.modified[name] = this.data[name];
12784 this.data[name] = value;
12785 if(!this.editing && this.store){
12786 this.store.afterEdit(this);
12791 * Get the value of the named field.
12792 * @param {String} name The name of the field to get the value of.
12793 * @return {Object} The value of the field.
12795 get : function(name){
12796 return this.data[name];
12800 beginEdit : function(){
12801 this.editing = true;
12802 this.modified = {};
12806 cancelEdit : function(){
12807 this.editing = false;
12808 delete this.modified;
12812 endEdit : function(){
12813 this.editing = false;
12814 if(this.dirty && this.store){
12815 this.store.afterEdit(this);
12820 * Usually called by the {@link Roo.data.Store} which owns the Record.
12821 * Rejects all changes made to the Record since either creation, or the last commit operation.
12822 * Modified fields are reverted to their original values.
12824 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12825 * of reject operations.
12827 reject : function(){
12828 var m = this.modified;
12830 if(typeof m[n] != "function"){
12831 this.data[n] = m[n];
12834 this.dirty = false;
12835 delete this.modified;
12836 this.editing = false;
12838 this.store.afterReject(this);
12843 * Usually called by the {@link Roo.data.Store} which owns the Record.
12844 * Commits all changes made to the Record since either creation, or the last commit operation.
12846 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12847 * of commit operations.
12849 commit : function(){
12850 this.dirty = false;
12851 delete this.modified;
12852 this.editing = false;
12854 this.store.afterCommit(this);
12859 hasError : function(){
12860 return this.error != null;
12864 clearError : function(){
12869 * Creates a copy of this record.
12870 * @param {String} id (optional) A new record id if you don't want to use this record's id
12873 copy : function(newId) {
12874 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12878 * Ext JS Library 1.1.1
12879 * Copyright(c) 2006-2007, Ext JS, LLC.
12881 * Originally Released Under LGPL - original licence link has changed is not relivant.
12884 * <script type="text/javascript">
12890 * @class Roo.data.Store
12891 * @extends Roo.util.Observable
12892 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12893 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12895 * 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
12896 * has no knowledge of the format of the data returned by the Proxy.<br>
12898 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12899 * instances from the data object. These records are cached and made available through accessor functions.
12901 * Creates a new Store.
12902 * @param {Object} config A config object containing the objects needed for the Store to access data,
12903 * and read the data into Records.
12905 Roo.data.Store = function(config){
12906 this.data = new Roo.util.MixedCollection(false);
12907 this.data.getKey = function(o){
12910 this.baseParams = {};
12912 this.paramNames = {
12917 "multisort" : "_multisort"
12920 if(config && config.data){
12921 this.inlineData = config.data;
12922 delete config.data;
12925 Roo.apply(this, config);
12927 if(this.reader){ // reader passed
12928 this.reader = Roo.factory(this.reader, Roo.data);
12929 this.reader.xmodule = this.xmodule || false;
12930 if(!this.recordType){
12931 this.recordType = this.reader.recordType;
12933 if(this.reader.onMetaChange){
12934 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12938 if(this.recordType){
12939 this.fields = this.recordType.prototype.fields;
12941 this.modified = [];
12945 * @event datachanged
12946 * Fires when the data cache has changed, and a widget which is using this Store
12947 * as a Record cache should refresh its view.
12948 * @param {Store} this
12950 datachanged : true,
12952 * @event metachange
12953 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12954 * @param {Store} this
12955 * @param {Object} meta The JSON metadata
12960 * Fires when Records have been added to the Store
12961 * @param {Store} this
12962 * @param {Roo.data.Record[]} records The array of Records added
12963 * @param {Number} index The index at which the record(s) were added
12968 * Fires when a Record has been removed from the Store
12969 * @param {Store} this
12970 * @param {Roo.data.Record} record The Record that was removed
12971 * @param {Number} index The index at which the record was removed
12976 * Fires when a Record has been updated
12977 * @param {Store} this
12978 * @param {Roo.data.Record} record The Record that was updated
12979 * @param {String} operation The update operation being performed. Value may be one of:
12981 Roo.data.Record.EDIT
12982 Roo.data.Record.REJECT
12983 Roo.data.Record.COMMIT
12989 * Fires when the data cache has been cleared.
12990 * @param {Store} this
12994 * @event beforeload
12995 * Fires before a request is made for a new data object. If the beforeload handler returns false
12996 * the load action will be canceled.
12997 * @param {Store} this
12998 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13002 * @event beforeloadadd
13003 * Fires after a new set of Records has been loaded.
13004 * @param {Store} this
13005 * @param {Roo.data.Record[]} records The Records that were loaded
13006 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13008 beforeloadadd : true,
13011 * Fires after a new set of Records has been loaded, before they are added to the store.
13012 * @param {Store} this
13013 * @param {Roo.data.Record[]} records The Records that were loaded
13014 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13015 * @params {Object} return from reader
13019 * @event loadexception
13020 * Fires if an exception occurs in the Proxy during loading.
13021 * Called with the signature of the Proxy's "loadexception" event.
13022 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13025 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13026 * @param {Object} load options
13027 * @param {Object} jsonData from your request (normally this contains the Exception)
13029 loadexception : true
13033 this.proxy = Roo.factory(this.proxy, Roo.data);
13034 this.proxy.xmodule = this.xmodule || false;
13035 this.relayEvents(this.proxy, ["loadexception"]);
13037 this.sortToggle = {};
13038 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13040 Roo.data.Store.superclass.constructor.call(this);
13042 if(this.inlineData){
13043 this.loadData(this.inlineData);
13044 delete this.inlineData;
13048 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13050 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13051 * without a remote query - used by combo/forms at present.
13055 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13058 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13061 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13062 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13065 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13066 * on any HTTP request
13069 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13072 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13076 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13077 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13079 remoteSort : false,
13082 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13083 * loaded or when a record is removed. (defaults to false).
13085 pruneModifiedRecords : false,
13088 lastOptions : null,
13091 * Add Records to the Store and fires the add event.
13092 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13094 add : function(records){
13095 records = [].concat(records);
13096 for(var i = 0, len = records.length; i < len; i++){
13097 records[i].join(this);
13099 var index = this.data.length;
13100 this.data.addAll(records);
13101 this.fireEvent("add", this, records, index);
13105 * Remove a Record from the Store and fires the remove event.
13106 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13108 remove : function(record){
13109 var index = this.data.indexOf(record);
13110 this.data.removeAt(index);
13112 if(this.pruneModifiedRecords){
13113 this.modified.remove(record);
13115 this.fireEvent("remove", this, record, index);
13119 * Remove all Records from the Store and fires the clear event.
13121 removeAll : function(){
13123 if(this.pruneModifiedRecords){
13124 this.modified = [];
13126 this.fireEvent("clear", this);
13130 * Inserts Records to the Store at the given index and fires the add event.
13131 * @param {Number} index The start index at which to insert the passed Records.
13132 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13134 insert : function(index, records){
13135 records = [].concat(records);
13136 for(var i = 0, len = records.length; i < len; i++){
13137 this.data.insert(index, records[i]);
13138 records[i].join(this);
13140 this.fireEvent("add", this, records, index);
13144 * Get the index within the cache of the passed Record.
13145 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13146 * @return {Number} The index of the passed Record. Returns -1 if not found.
13148 indexOf : function(record){
13149 return this.data.indexOf(record);
13153 * Get the index within the cache of the Record with the passed id.
13154 * @param {String} id The id of the Record to find.
13155 * @return {Number} The index of the Record. Returns -1 if not found.
13157 indexOfId : function(id){
13158 return this.data.indexOfKey(id);
13162 * Get the Record with the specified id.
13163 * @param {String} id The id of the Record to find.
13164 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13166 getById : function(id){
13167 return this.data.key(id);
13171 * Get the Record at the specified index.
13172 * @param {Number} index The index of the Record to find.
13173 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13175 getAt : function(index){
13176 return this.data.itemAt(index);
13180 * Returns a range of Records between specified indices.
13181 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13182 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13183 * @return {Roo.data.Record[]} An array of Records
13185 getRange : function(start, end){
13186 return this.data.getRange(start, end);
13190 storeOptions : function(o){
13191 o = Roo.apply({}, o);
13194 this.lastOptions = o;
13198 * Loads the Record cache from the configured Proxy using the configured Reader.
13200 * If using remote paging, then the first load call must specify the <em>start</em>
13201 * and <em>limit</em> properties in the options.params property to establish the initial
13202 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13204 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13205 * and this call will return before the new data has been loaded. Perform any post-processing
13206 * in a callback function, or in a "load" event handler.</strong>
13208 * @param {Object} options An object containing properties which control loading options:<ul>
13209 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13210 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13211 * passed the following arguments:<ul>
13212 * <li>r : Roo.data.Record[]</li>
13213 * <li>options: Options object from the load call</li>
13214 * <li>success: Boolean success indicator</li></ul></li>
13215 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13216 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13219 load : function(options){
13220 options = options || {};
13221 if(this.fireEvent("beforeload", this, options) !== false){
13222 this.storeOptions(options);
13223 var p = Roo.apply(options.params || {}, this.baseParams);
13224 // if meta was not loaded from remote source.. try requesting it.
13225 if (!this.reader.metaFromRemote) {
13226 p._requestMeta = 1;
13228 if(this.sortInfo && this.remoteSort){
13229 var pn = this.paramNames;
13230 p[pn["sort"]] = this.sortInfo.field;
13231 p[pn["dir"]] = this.sortInfo.direction;
13233 if (this.multiSort) {
13234 var pn = this.paramNames;
13235 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13238 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13243 * Reloads the Record cache from the configured Proxy using the configured Reader and
13244 * the options from the last load operation performed.
13245 * @param {Object} options (optional) An object containing properties which may override the options
13246 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13247 * the most recently used options are reused).
13249 reload : function(options){
13250 this.load(Roo.applyIf(options||{}, this.lastOptions));
13254 // Called as a callback by the Reader during a load operation.
13255 loadRecords : function(o, options, success){
13256 if(!o || success === false){
13257 if(success !== false){
13258 this.fireEvent("load", this, [], options, o);
13260 if(options.callback){
13261 options.callback.call(options.scope || this, [], options, false);
13265 // if data returned failure - throw an exception.
13266 if (o.success === false) {
13267 // show a message if no listener is registered.
13268 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13269 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13271 // loadmask wil be hooked into this..
13272 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13275 var r = o.records, t = o.totalRecords || r.length;
13277 this.fireEvent("beforeloadadd", this, r, options, o);
13279 if(!options || options.add !== true){
13280 if(this.pruneModifiedRecords){
13281 this.modified = [];
13283 for(var i = 0, len = r.length; i < len; i++){
13287 this.data = this.snapshot;
13288 delete this.snapshot;
13291 this.data.addAll(r);
13292 this.totalLength = t;
13294 this.fireEvent("datachanged", this);
13296 this.totalLength = Math.max(t, this.data.length+r.length);
13300 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13302 var e = new Roo.data.Record({});
13304 e.set(this.parent.displayField, this.parent.emptyTitle);
13305 e.set(this.parent.valueField, '');
13310 this.fireEvent("load", this, r, options, o);
13311 if(options.callback){
13312 options.callback.call(options.scope || this, r, options, true);
13318 * Loads data from a passed data block. A Reader which understands the format of the data
13319 * must have been configured in the constructor.
13320 * @param {Object} data The data block from which to read the Records. The format of the data expected
13321 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13322 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13324 loadData : function(o, append){
13325 var r = this.reader.readRecords(o);
13326 this.loadRecords(r, {add: append}, true);
13330 * using 'cn' the nested child reader read the child array into it's child stores.
13331 * @param {Object} rec The record with a 'children array
13333 loadDataFromChildren : function(rec)
13335 this.loadData(this.reader.toLoadData(rec));
13340 * Gets the number of cached records.
13342 * <em>If using paging, this may not be the total size of the dataset. If the data object
13343 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13344 * the data set size</em>
13346 getCount : function(){
13347 return this.data.length || 0;
13351 * Gets the total number of records in the dataset as returned by the server.
13353 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13354 * the dataset size</em>
13356 getTotalCount : function(){
13357 return this.totalLength || 0;
13361 * Returns the sort state of the Store as an object with two properties:
13363 field {String} The name of the field by which the Records are sorted
13364 direction {String} The sort order, "ASC" or "DESC"
13367 getSortState : function(){
13368 return this.sortInfo;
13372 applySort : function(){
13373 if(this.sortInfo && !this.remoteSort){
13374 var s = this.sortInfo, f = s.field;
13375 var st = this.fields.get(f).sortType;
13376 var fn = function(r1, r2){
13377 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13378 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13380 this.data.sort(s.direction, fn);
13381 if(this.snapshot && this.snapshot != this.data){
13382 this.snapshot.sort(s.direction, fn);
13388 * Sets the default sort column and order to be used by the next load operation.
13389 * @param {String} fieldName The name of the field to sort by.
13390 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13392 setDefaultSort : function(field, dir){
13393 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13397 * Sort the Records.
13398 * If remote sorting is used, the sort is performed on the server, and the cache is
13399 * reloaded. If local sorting is used, the cache is sorted internally.
13400 * @param {String} fieldName The name of the field to sort by.
13401 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13403 sort : function(fieldName, dir){
13404 var f = this.fields.get(fieldName);
13406 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13408 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13409 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13414 this.sortToggle[f.name] = dir;
13415 this.sortInfo = {field: f.name, direction: dir};
13416 if(!this.remoteSort){
13418 this.fireEvent("datachanged", this);
13420 this.load(this.lastOptions);
13425 * Calls the specified function for each of the Records in the cache.
13426 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13427 * Returning <em>false</em> aborts and exits the iteration.
13428 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13430 each : function(fn, scope){
13431 this.data.each(fn, scope);
13435 * Gets all records modified since the last commit. Modified records are persisted across load operations
13436 * (e.g., during paging).
13437 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13439 getModifiedRecords : function(){
13440 return this.modified;
13444 createFilterFn : function(property, value, anyMatch){
13445 if(!value.exec){ // not a regex
13446 value = String(value);
13447 if(value.length == 0){
13450 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13452 return function(r){
13453 return value.test(r.data[property]);
13458 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13459 * @param {String} property A field on your records
13460 * @param {Number} start The record index to start at (defaults to 0)
13461 * @param {Number} end The last record index to include (defaults to length - 1)
13462 * @return {Number} The sum
13464 sum : function(property, start, end){
13465 var rs = this.data.items, v = 0;
13466 start = start || 0;
13467 end = (end || end === 0) ? end : rs.length-1;
13469 for(var i = start; i <= end; i++){
13470 v += (rs[i].data[property] || 0);
13476 * Filter the records by a specified property.
13477 * @param {String} field A field on your records
13478 * @param {String/RegExp} value Either a string that the field
13479 * should start with or a RegExp to test against the field
13480 * @param {Boolean} anyMatch True to match any part not just the beginning
13482 filter : function(property, value, anyMatch){
13483 var fn = this.createFilterFn(property, value, anyMatch);
13484 return fn ? this.filterBy(fn) : this.clearFilter();
13488 * Filter by a function. The specified function will be called with each
13489 * record in this data source. If the function returns true the record is included,
13490 * otherwise it is filtered.
13491 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13492 * @param {Object} scope (optional) The scope of the function (defaults to this)
13494 filterBy : function(fn, scope){
13495 this.snapshot = this.snapshot || this.data;
13496 this.data = this.queryBy(fn, scope||this);
13497 this.fireEvent("datachanged", this);
13501 * Query the records by a specified property.
13502 * @param {String} field A field on your records
13503 * @param {String/RegExp} value Either a string that the field
13504 * should start with or a RegExp to test against the field
13505 * @param {Boolean} anyMatch True to match any part not just the beginning
13506 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13508 query : function(property, value, anyMatch){
13509 var fn = this.createFilterFn(property, value, anyMatch);
13510 return fn ? this.queryBy(fn) : this.data.clone();
13514 * Query by a function. The specified function will be called with each
13515 * record in this data source. If the function returns true the record is included
13517 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13518 * @param {Object} scope (optional) The scope of the function (defaults to this)
13519 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13521 queryBy : function(fn, scope){
13522 var data = this.snapshot || this.data;
13523 return data.filterBy(fn, scope||this);
13527 * Collects unique values for a particular dataIndex from this store.
13528 * @param {String} dataIndex The property to collect
13529 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13530 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13531 * @return {Array} An array of the unique values
13533 collect : function(dataIndex, allowNull, bypassFilter){
13534 var d = (bypassFilter === true && this.snapshot) ?
13535 this.snapshot.items : this.data.items;
13536 var v, sv, r = [], l = {};
13537 for(var i = 0, len = d.length; i < len; i++){
13538 v = d[i].data[dataIndex];
13540 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13549 * Revert to a view of the Record cache with no filtering applied.
13550 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13552 clearFilter : function(suppressEvent){
13553 if(this.snapshot && this.snapshot != this.data){
13554 this.data = this.snapshot;
13555 delete this.snapshot;
13556 if(suppressEvent !== true){
13557 this.fireEvent("datachanged", this);
13563 afterEdit : function(record){
13564 if(this.modified.indexOf(record) == -1){
13565 this.modified.push(record);
13567 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13571 afterReject : function(record){
13572 this.modified.remove(record);
13573 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13577 afterCommit : function(record){
13578 this.modified.remove(record);
13579 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13583 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13584 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13586 commitChanges : function(){
13587 var m = this.modified.slice(0);
13588 this.modified = [];
13589 for(var i = 0, len = m.length; i < len; i++){
13595 * Cancel outstanding changes on all changed records.
13597 rejectChanges : function(){
13598 var m = this.modified.slice(0);
13599 this.modified = [];
13600 for(var i = 0, len = m.length; i < len; i++){
13605 onMetaChange : function(meta, rtype, o){
13606 this.recordType = rtype;
13607 this.fields = rtype.prototype.fields;
13608 delete this.snapshot;
13609 this.sortInfo = meta.sortInfo || this.sortInfo;
13610 this.modified = [];
13611 this.fireEvent('metachange', this, this.reader.meta);
13614 moveIndex : function(data, type)
13616 var index = this.indexOf(data);
13618 var newIndex = index + type;
13622 this.insert(newIndex, data);
13627 * Ext JS Library 1.1.1
13628 * Copyright(c) 2006-2007, Ext JS, LLC.
13630 * Originally Released Under LGPL - original licence link has changed is not relivant.
13633 * <script type="text/javascript">
13637 * @class Roo.data.SimpleStore
13638 * @extends Roo.data.Store
13639 * Small helper class to make creating Stores from Array data easier.
13640 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13641 * @cfg {Array} fields An array of field definition objects, or field name strings.
13642 * @cfg {Object} an existing reader (eg. copied from another store)
13643 * @cfg {Array} data The multi-dimensional array of data
13645 * @param {Object} config
13647 Roo.data.SimpleStore = function(config)
13649 Roo.data.SimpleStore.superclass.constructor.call(this, {
13651 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13654 Roo.data.Record.create(config.fields)
13656 proxy : new Roo.data.MemoryProxy(config.data)
13660 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13662 * Ext JS Library 1.1.1
13663 * Copyright(c) 2006-2007, Ext JS, LLC.
13665 * Originally Released Under LGPL - original licence link has changed is not relivant.
13668 * <script type="text/javascript">
13673 * @extends Roo.data.Store
13674 * @class Roo.data.JsonStore
13675 * Small helper class to make creating Stores for JSON data easier. <br/>
13677 var store = new Roo.data.JsonStore({
13678 url: 'get-images.php',
13680 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13683 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13684 * JsonReader and HttpProxy (unless inline data is provided).</b>
13685 * @cfg {Array} fields An array of field definition objects, or field name strings.
13687 * @param {Object} config
13689 Roo.data.JsonStore = function(c){
13690 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13691 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13692 reader: new Roo.data.JsonReader(c, c.fields)
13695 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13697 * Ext JS Library 1.1.1
13698 * Copyright(c) 2006-2007, Ext JS, LLC.
13700 * Originally Released Under LGPL - original licence link has changed is not relivant.
13703 * <script type="text/javascript">
13707 Roo.data.Field = function(config){
13708 if(typeof config == "string"){
13709 config = {name: config};
13711 Roo.apply(this, config);
13714 this.type = "auto";
13717 var st = Roo.data.SortTypes;
13718 // named sortTypes are supported, here we look them up
13719 if(typeof this.sortType == "string"){
13720 this.sortType = st[this.sortType];
13723 // set default sortType for strings and dates
13724 if(!this.sortType){
13727 this.sortType = st.asUCString;
13730 this.sortType = st.asDate;
13733 this.sortType = st.none;
13738 var stripRe = /[\$,%]/g;
13740 // prebuilt conversion function for this field, instead of
13741 // switching every time we're reading a value
13743 var cv, dateFormat = this.dateFormat;
13748 cv = function(v){ return v; };
13751 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13755 return v !== undefined && v !== null && v !== '' ?
13756 parseInt(String(v).replace(stripRe, ""), 10) : '';
13761 return v !== undefined && v !== null && v !== '' ?
13762 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13767 cv = function(v){ return v === true || v === "true" || v == 1; };
13774 if(v instanceof Date){
13778 if(dateFormat == "timestamp"){
13779 return new Date(v*1000);
13781 return Date.parseDate(v, dateFormat);
13783 var parsed = Date.parse(v);
13784 return parsed ? new Date(parsed) : null;
13793 Roo.data.Field.prototype = {
13801 * Ext JS Library 1.1.1
13802 * Copyright(c) 2006-2007, Ext JS, LLC.
13804 * Originally Released Under LGPL - original licence link has changed is not relivant.
13807 * <script type="text/javascript">
13810 // Base class for reading structured data from a data source. This class is intended to be
13811 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13814 * @class Roo.data.DataReader
13815 * Base class for reading structured data from a data source. This class is intended to be
13816 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13819 Roo.data.DataReader = function(meta, recordType){
13823 this.recordType = recordType instanceof Array ?
13824 Roo.data.Record.create(recordType) : recordType;
13827 Roo.data.DataReader.prototype = {
13830 readerType : 'Data',
13832 * Create an empty record
13833 * @param {Object} data (optional) - overlay some values
13834 * @return {Roo.data.Record} record created.
13836 newRow : function(d) {
13838 this.recordType.prototype.fields.each(function(c) {
13840 case 'int' : da[c.name] = 0; break;
13841 case 'date' : da[c.name] = new Date(); break;
13842 case 'float' : da[c.name] = 0.0; break;
13843 case 'boolean' : da[c.name] = false; break;
13844 default : da[c.name] = ""; break;
13848 return new this.recordType(Roo.apply(da, d));
13854 * Ext JS Library 1.1.1
13855 * Copyright(c) 2006-2007, Ext JS, LLC.
13857 * Originally Released Under LGPL - original licence link has changed is not relivant.
13860 * <script type="text/javascript">
13864 * @class Roo.data.DataProxy
13865 * @extends Roo.data.Observable
13866 * This class is an abstract base class for implementations which provide retrieval of
13867 * unformatted data objects.<br>
13869 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13870 * (of the appropriate type which knows how to parse the data object) to provide a block of
13871 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13873 * Custom implementations must implement the load method as described in
13874 * {@link Roo.data.HttpProxy#load}.
13876 Roo.data.DataProxy = function(){
13879 * @event beforeload
13880 * Fires before a network request is made to retrieve a data object.
13881 * @param {Object} This DataProxy object.
13882 * @param {Object} params The params parameter to the load function.
13887 * Fires before the load method's callback is called.
13888 * @param {Object} This DataProxy object.
13889 * @param {Object} o The data object.
13890 * @param {Object} arg The callback argument object passed to the load function.
13894 * @event loadexception
13895 * Fires if an Exception occurs during data retrieval.
13896 * @param {Object} This DataProxy object.
13897 * @param {Object} o The data object.
13898 * @param {Object} arg The callback argument object passed to the load function.
13899 * @param {Object} e The Exception.
13901 loadexception : true
13903 Roo.data.DataProxy.superclass.constructor.call(this);
13906 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13909 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13913 * Ext JS Library 1.1.1
13914 * Copyright(c) 2006-2007, Ext JS, LLC.
13916 * Originally Released Under LGPL - original licence link has changed is not relivant.
13919 * <script type="text/javascript">
13922 * @class Roo.data.MemoryProxy
13923 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13924 * to the Reader when its load method is called.
13926 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13928 Roo.data.MemoryProxy = function(data){
13932 Roo.data.MemoryProxy.superclass.constructor.call(this);
13936 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13939 * Load data from the requested source (in this case an in-memory
13940 * data object passed to the constructor), read the data object into
13941 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13942 * process that block using the passed callback.
13943 * @param {Object} params This parameter is not used by the MemoryProxy class.
13944 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13945 * object into a block of Roo.data.Records.
13946 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13947 * The function must be passed <ul>
13948 * <li>The Record block object</li>
13949 * <li>The "arg" argument from the load function</li>
13950 * <li>A boolean success indicator</li>
13952 * @param {Object} scope The scope in which to call the callback
13953 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13955 load : function(params, reader, callback, scope, arg){
13956 params = params || {};
13959 result = reader.readRecords(params.data ? params.data :this.data);
13961 this.fireEvent("loadexception", this, arg, null, e);
13962 callback.call(scope, null, arg, false);
13965 callback.call(scope, result, arg, true);
13969 update : function(params, records){
13974 * Ext JS Library 1.1.1
13975 * Copyright(c) 2006-2007, Ext JS, LLC.
13977 * Originally Released Under LGPL - original licence link has changed is not relivant.
13980 * <script type="text/javascript">
13983 * @class Roo.data.HttpProxy
13984 * @extends Roo.data.DataProxy
13985 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13986 * configured to reference a certain URL.<br><br>
13988 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13989 * from which the running page was served.<br><br>
13991 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13993 * Be aware that to enable the browser to parse an XML document, the server must set
13994 * the Content-Type header in the HTTP response to "text/xml".
13996 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13997 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
13998 * will be used to make the request.
14000 Roo.data.HttpProxy = function(conn){
14001 Roo.data.HttpProxy.superclass.constructor.call(this);
14002 // is conn a conn config or a real conn?
14004 this.useAjax = !conn || !conn.events;
14008 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14009 // thse are take from connection...
14012 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14015 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14016 * extra parameters to each request made by this object. (defaults to undefined)
14019 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14020 * to each request made by this object. (defaults to undefined)
14023 * @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)
14026 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14029 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14035 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14039 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14040 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14041 * a finer-grained basis than the DataProxy events.
14043 getConnection : function(){
14044 return this.useAjax ? Roo.Ajax : this.conn;
14048 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14049 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14050 * process that block using the passed callback.
14051 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14052 * for the request to the remote server.
14053 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14054 * object into a block of Roo.data.Records.
14055 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14056 * The function must be passed <ul>
14057 * <li>The Record block object</li>
14058 * <li>The "arg" argument from the load function</li>
14059 * <li>A boolean success indicator</li>
14061 * @param {Object} scope The scope in which to call the callback
14062 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14064 load : function(params, reader, callback, scope, arg){
14065 if(this.fireEvent("beforeload", this, params) !== false){
14067 params : params || {},
14069 callback : callback,
14074 callback : this.loadResponse,
14078 Roo.applyIf(o, this.conn);
14079 if(this.activeRequest){
14080 Roo.Ajax.abort(this.activeRequest);
14082 this.activeRequest = Roo.Ajax.request(o);
14084 this.conn.request(o);
14087 callback.call(scope||this, null, arg, false);
14092 loadResponse : function(o, success, response){
14093 delete this.activeRequest;
14095 this.fireEvent("loadexception", this, o, response);
14096 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14101 result = o.reader.read(response);
14103 this.fireEvent("loadexception", this, o, response, e);
14104 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14108 this.fireEvent("load", this, o, o.request.arg);
14109 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14113 update : function(dataSet){
14118 updateResponse : function(dataSet){
14123 * Ext JS Library 1.1.1
14124 * Copyright(c) 2006-2007, Ext JS, LLC.
14126 * Originally Released Under LGPL - original licence link has changed is not relivant.
14129 * <script type="text/javascript">
14133 * @class Roo.data.ScriptTagProxy
14134 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14135 * other than the originating domain of the running page.<br><br>
14137 * <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
14138 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14140 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14141 * source code that is used as the source inside a <script> tag.<br><br>
14143 * In order for the browser to process the returned data, the server must wrap the data object
14144 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14145 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14146 * depending on whether the callback name was passed:
14149 boolean scriptTag = false;
14150 String cb = request.getParameter("callback");
14153 response.setContentType("text/javascript");
14155 response.setContentType("application/x-json");
14157 Writer out = response.getWriter();
14159 out.write(cb + "(");
14161 out.print(dataBlock.toJsonString());
14168 * @param {Object} config A configuration object.
14170 Roo.data.ScriptTagProxy = function(config){
14171 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14172 Roo.apply(this, config);
14173 this.head = document.getElementsByTagName("head")[0];
14176 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14178 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14180 * @cfg {String} url The URL from which to request the data object.
14183 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14187 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14188 * the server the name of the callback function set up by the load call to process the returned data object.
14189 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14190 * javascript output which calls this named function passing the data object as its only parameter.
14192 callbackParam : "callback",
14194 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14195 * name to the request.
14200 * Load data from the configured URL, read the data object into
14201 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14202 * process that block using the passed callback.
14203 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14204 * for the request to the remote server.
14205 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14206 * object into a block of Roo.data.Records.
14207 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14208 * The function must be passed <ul>
14209 * <li>The Record block object</li>
14210 * <li>The "arg" argument from the load function</li>
14211 * <li>A boolean success indicator</li>
14213 * @param {Object} scope The scope in which to call the callback
14214 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14216 load : function(params, reader, callback, scope, arg){
14217 if(this.fireEvent("beforeload", this, params) !== false){
14219 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14221 var url = this.url;
14222 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14224 url += "&_dc=" + (new Date().getTime());
14226 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14229 cb : "stcCallback"+transId,
14230 scriptId : "stcScript"+transId,
14234 callback : callback,
14240 window[trans.cb] = function(o){
14241 conn.handleResponse(o, trans);
14244 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14246 if(this.autoAbort !== false){
14250 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14252 var script = document.createElement("script");
14253 script.setAttribute("src", url);
14254 script.setAttribute("type", "text/javascript");
14255 script.setAttribute("id", trans.scriptId);
14256 this.head.appendChild(script);
14258 this.trans = trans;
14260 callback.call(scope||this, null, arg, false);
14265 isLoading : function(){
14266 return this.trans ? true : false;
14270 * Abort the current server request.
14272 abort : function(){
14273 if(this.isLoading()){
14274 this.destroyTrans(this.trans);
14279 destroyTrans : function(trans, isLoaded){
14280 this.head.removeChild(document.getElementById(trans.scriptId));
14281 clearTimeout(trans.timeoutId);
14283 window[trans.cb] = undefined;
14285 delete window[trans.cb];
14288 // if hasn't been loaded, wait for load to remove it to prevent script error
14289 window[trans.cb] = function(){
14290 window[trans.cb] = undefined;
14292 delete window[trans.cb];
14299 handleResponse : function(o, trans){
14300 this.trans = false;
14301 this.destroyTrans(trans, true);
14304 result = trans.reader.readRecords(o);
14306 this.fireEvent("loadexception", this, o, trans.arg, e);
14307 trans.callback.call(trans.scope||window, null, trans.arg, false);
14310 this.fireEvent("load", this, o, trans.arg);
14311 trans.callback.call(trans.scope||window, result, trans.arg, true);
14315 handleFailure : function(trans){
14316 this.trans = false;
14317 this.destroyTrans(trans, false);
14318 this.fireEvent("loadexception", this, null, trans.arg);
14319 trans.callback.call(trans.scope||window, null, trans.arg, false);
14323 * Ext JS Library 1.1.1
14324 * Copyright(c) 2006-2007, Ext JS, LLC.
14326 * Originally Released Under LGPL - original licence link has changed is not relivant.
14329 * <script type="text/javascript">
14333 * @class Roo.data.JsonReader
14334 * @extends Roo.data.DataReader
14335 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14336 * based on mappings in a provided Roo.data.Record constructor.
14338 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14339 * in the reply previously.
14344 var RecordDef = Roo.data.Record.create([
14345 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14346 {name: 'occupation'} // This field will use "occupation" as the mapping.
14348 var myReader = new Roo.data.JsonReader({
14349 totalProperty: "results", // The property which contains the total dataset size (optional)
14350 root: "rows", // The property which contains an Array of row objects
14351 id: "id" // The property within each row object that provides an ID for the record (optional)
14355 * This would consume a JSON file like this:
14357 { 'results': 2, 'rows': [
14358 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14359 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14362 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14363 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14364 * paged from the remote server.
14365 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14366 * @cfg {String} root name of the property which contains the Array of row objects.
14367 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14368 * @cfg {Array} fields Array of field definition objects
14370 * Create a new JsonReader
14371 * @param {Object} meta Metadata configuration options
14372 * @param {Object} recordType Either an Array of field definition objects,
14373 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14375 Roo.data.JsonReader = function(meta, recordType){
14378 // set some defaults:
14379 Roo.applyIf(meta, {
14380 totalProperty: 'total',
14381 successProperty : 'success',
14386 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14388 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14390 readerType : 'Json',
14393 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14394 * Used by Store query builder to append _requestMeta to params.
14397 metaFromRemote : false,
14399 * This method is only used by a DataProxy which has retrieved data from a remote server.
14400 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14401 * @return {Object} data A data block which is used by an Roo.data.Store object as
14402 * a cache of Roo.data.Records.
14404 read : function(response){
14405 var json = response.responseText;
14407 var o = /* eval:var:o */ eval("("+json+")");
14409 throw {message: "JsonReader.read: Json object not found"};
14415 this.metaFromRemote = true;
14416 this.meta = o.metaData;
14417 this.recordType = Roo.data.Record.create(o.metaData.fields);
14418 this.onMetaChange(this.meta, this.recordType, o);
14420 return this.readRecords(o);
14423 // private function a store will implement
14424 onMetaChange : function(meta, recordType, o){
14431 simpleAccess: function(obj, subsc) {
14438 getJsonAccessor: function(){
14440 return function(expr) {
14442 return(re.test(expr))
14443 ? new Function("obj", "return obj." + expr)
14448 return Roo.emptyFn;
14453 * Create a data block containing Roo.data.Records from an XML document.
14454 * @param {Object} o An object which contains an Array of row objects in the property specified
14455 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14456 * which contains the total size of the dataset.
14457 * @return {Object} data A data block which is used by an Roo.data.Store object as
14458 * a cache of Roo.data.Records.
14460 readRecords : function(o){
14462 * After any data loads, the raw JSON data is available for further custom processing.
14466 var s = this.meta, Record = this.recordType,
14467 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14469 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14471 if(s.totalProperty) {
14472 this.getTotal = this.getJsonAccessor(s.totalProperty);
14474 if(s.successProperty) {
14475 this.getSuccess = this.getJsonAccessor(s.successProperty);
14477 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14479 var g = this.getJsonAccessor(s.id);
14480 this.getId = function(rec) {
14482 return (r === undefined || r === "") ? null : r;
14485 this.getId = function(){return null;};
14488 for(var jj = 0; jj < fl; jj++){
14490 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14491 this.ef[jj] = this.getJsonAccessor(map);
14495 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14496 if(s.totalProperty){
14497 var vt = parseInt(this.getTotal(o), 10);
14502 if(s.successProperty){
14503 var vs = this.getSuccess(o);
14504 if(vs === false || vs === 'false'){
14509 for(var i = 0; i < c; i++){
14512 var id = this.getId(n);
14513 for(var j = 0; j < fl; j++){
14515 var v = this.ef[j](n);
14517 Roo.log('missing convert for ' + f.name);
14521 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14523 var record = new Record(values, id);
14525 records[i] = record;
14531 totalRecords : totalRecords
14534 // used when loading children.. @see loadDataFromChildren
14535 toLoadData: function(rec)
14537 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14538 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14539 return { data : data, total : data.length };
14544 * Ext JS Library 1.1.1
14545 * Copyright(c) 2006-2007, Ext JS, LLC.
14547 * Originally Released Under LGPL - original licence link has changed is not relivant.
14550 * <script type="text/javascript">
14554 * @class Roo.data.ArrayReader
14555 * @extends Roo.data.DataReader
14556 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14557 * Each element of that Array represents a row of data fields. The
14558 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14559 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14563 var RecordDef = Roo.data.Record.create([
14564 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14565 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14567 var myReader = new Roo.data.ArrayReader({
14568 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14572 * This would consume an Array like this:
14574 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14578 * Create a new JsonReader
14579 * @param {Object} meta Metadata configuration options.
14580 * @param {Object|Array} recordType Either an Array of field definition objects
14582 * @cfg {Array} fields Array of field definition objects
14583 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14584 * as specified to {@link Roo.data.Record#create},
14585 * or an {@link Roo.data.Record} object
14588 * created using {@link Roo.data.Record#create}.
14590 Roo.data.ArrayReader = function(meta, recordType)
14592 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14595 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14598 * Create a data block containing Roo.data.Records from an XML document.
14599 * @param {Object} o An Array of row objects which represents the dataset.
14600 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14601 * a cache of Roo.data.Records.
14603 readRecords : function(o)
14605 var sid = this.meta ? this.meta.id : null;
14606 var recordType = this.recordType, fields = recordType.prototype.fields;
14609 for(var i = 0; i < root.length; i++){
14612 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14613 for(var j = 0, jlen = fields.length; j < jlen; j++){
14614 var f = fields.items[j];
14615 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14616 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14618 values[f.name] = v;
14620 var record = new recordType(values, id);
14622 records[records.length] = record;
14626 totalRecords : records.length
14629 // used when loading children.. @see loadDataFromChildren
14630 toLoadData: function(rec)
14632 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14633 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14644 * @class Roo.bootstrap.ComboBox
14645 * @extends Roo.bootstrap.TriggerField
14646 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14647 * @cfg {Boolean} append (true|false) default false
14648 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14649 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14650 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14651 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14652 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14653 * @cfg {Boolean} animate default true
14654 * @cfg {Boolean} emptyResultText only for touch device
14655 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14656 * @cfg {String} emptyTitle default ''
14658 * Create a new ComboBox.
14659 * @param {Object} config Configuration options
14661 Roo.bootstrap.ComboBox = function(config){
14662 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14666 * Fires when the dropdown list is expanded
14667 * @param {Roo.bootstrap.ComboBox} combo This combo box
14672 * Fires when the dropdown list is collapsed
14673 * @param {Roo.bootstrap.ComboBox} combo This combo box
14677 * @event beforeselect
14678 * Fires before a list item is selected. Return false to cancel the selection.
14679 * @param {Roo.bootstrap.ComboBox} combo This combo box
14680 * @param {Roo.data.Record} record The data record returned from the underlying store
14681 * @param {Number} index The index of the selected item in the dropdown list
14683 'beforeselect' : true,
14686 * Fires when a list item is selected
14687 * @param {Roo.bootstrap.ComboBox} combo This combo box
14688 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14689 * @param {Number} index The index of the selected item in the dropdown list
14693 * @event beforequery
14694 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14695 * The event object passed has these properties:
14696 * @param {Roo.bootstrap.ComboBox} combo This combo box
14697 * @param {String} query The query
14698 * @param {Boolean} forceAll true to force "all" query
14699 * @param {Boolean} cancel true to cancel the query
14700 * @param {Object} e The query event object
14702 'beforequery': true,
14705 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14706 * @param {Roo.bootstrap.ComboBox} combo This combo box
14711 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14712 * @param {Roo.bootstrap.ComboBox} combo This combo box
14713 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14718 * Fires when the remove value from the combobox array
14719 * @param {Roo.bootstrap.ComboBox} combo This combo box
14723 * @event afterremove
14724 * Fires when the remove value from the combobox array
14725 * @param {Roo.bootstrap.ComboBox} combo This combo box
14727 'afterremove' : true,
14729 * @event specialfilter
14730 * Fires when specialfilter
14731 * @param {Roo.bootstrap.ComboBox} combo This combo box
14733 'specialfilter' : true,
14736 * Fires when tick the element
14737 * @param {Roo.bootstrap.ComboBox} combo This combo box
14741 * @event touchviewdisplay
14742 * Fires when touch view require special display (default is using displayField)
14743 * @param {Roo.bootstrap.ComboBox} combo This combo box
14744 * @param {Object} cfg set html .
14746 'touchviewdisplay' : true
14751 this.tickItems = [];
14753 this.selectedIndex = -1;
14754 if(this.mode == 'local'){
14755 if(config.queryDelay === undefined){
14756 this.queryDelay = 10;
14758 if(config.minChars === undefined){
14764 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14767 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14768 * rendering into an Roo.Editor, defaults to false)
14771 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14772 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14775 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14778 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14779 * the dropdown list (defaults to undefined, with no header element)
14783 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14787 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14789 listWidth: undefined,
14791 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14792 * mode = 'remote' or 'text' if mode = 'local')
14794 displayField: undefined,
14797 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14798 * mode = 'remote' or 'value' if mode = 'local').
14799 * Note: use of a valueField requires the user make a selection
14800 * in order for a value to be mapped.
14802 valueField: undefined,
14804 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14809 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14810 * field's data value (defaults to the underlying DOM element's name)
14812 hiddenName: undefined,
14814 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14818 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14820 selectedClass: 'active',
14823 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14827 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14828 * anchor positions (defaults to 'tl-bl')
14830 listAlign: 'tl-bl?',
14832 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14836 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14837 * query specified by the allQuery config option (defaults to 'query')
14839 triggerAction: 'query',
14841 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14842 * (defaults to 4, does not apply if editable = false)
14846 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14847 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14851 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14852 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14856 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14857 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14861 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14862 * when editable = true (defaults to false)
14864 selectOnFocus:false,
14866 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14868 queryParam: 'query',
14870 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14871 * when mode = 'remote' (defaults to 'Loading...')
14873 loadingText: 'Loading...',
14875 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14879 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14883 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14884 * traditional select (defaults to true)
14888 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14892 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14896 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14897 * listWidth has a higher value)
14901 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14902 * allow the user to set arbitrary text into the field (defaults to false)
14904 forceSelection:false,
14906 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14907 * if typeAhead = true (defaults to 250)
14909 typeAheadDelay : 250,
14911 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14912 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14914 valueNotFoundText : undefined,
14916 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14918 blockFocus : false,
14921 * @cfg {Boolean} disableClear Disable showing of clear button.
14923 disableClear : false,
14925 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14927 alwaysQuery : false,
14930 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14935 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14937 invalidClass : "has-warning",
14940 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14942 validClass : "has-success",
14945 * @cfg {Boolean} specialFilter (true|false) special filter default false
14947 specialFilter : false,
14950 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14952 mobileTouchView : true,
14955 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14957 useNativeIOS : false,
14960 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14962 mobile_restrict_height : false,
14964 ios_options : false,
14976 btnPosition : 'right',
14977 triggerList : true,
14978 showToggleBtn : true,
14980 emptyResultText: 'Empty',
14981 triggerText : 'Select',
14984 // element that contains real text value.. (when hidden is used..)
14986 getAutoCreate : function()
14991 * Render classic select for iso
14994 if(Roo.isIOS && this.useNativeIOS){
14995 cfg = this.getAutoCreateNativeIOS();
15003 if(Roo.isTouch && this.mobileTouchView){
15004 cfg = this.getAutoCreateTouchView();
15011 if(!this.tickable){
15012 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15017 * ComboBox with tickable selections
15020 var align = this.labelAlign || this.parentLabelAlign();
15023 cls : 'form-group roo-combobox-tickable' //input-group
15026 var btn_text_select = '';
15027 var btn_text_done = '';
15028 var btn_text_cancel = '';
15030 if (this.btn_text_show) {
15031 btn_text_select = 'Select';
15032 btn_text_done = 'Done';
15033 btn_text_cancel = 'Cancel';
15038 cls : 'tickable-buttons',
15043 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15044 //html : this.triggerText
15045 html: btn_text_select
15051 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15053 html: btn_text_done
15059 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15061 html: btn_text_cancel
15067 buttons.cn.unshift({
15069 cls: 'roo-select2-search-field-input'
15075 Roo.each(buttons.cn, function(c){
15077 c.cls += ' btn-' + _this.size;
15080 if (_this.disabled) {
15087 style : 'display: contents',
15092 cls: 'form-hidden-field'
15096 cls: 'roo-select2-choices',
15100 cls: 'roo-select2-search-field',
15111 cls: 'roo-select2-container input-group roo-select2-container-multi',
15117 // cls: 'typeahead typeahead-long dropdown-menu',
15118 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15123 if(this.hasFeedback && !this.allowBlank){
15127 cls: 'glyphicon form-control-feedback'
15130 combobox.cn.push(feedback);
15137 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15138 tooltip : 'This field is required'
15140 if (Roo.bootstrap.version == 4) {
15143 style : 'display:none'
15146 if (align ==='left' && this.fieldLabel.length) {
15148 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15155 cls : 'control-label col-form-label',
15156 html : this.fieldLabel
15168 var labelCfg = cfg.cn[1];
15169 var contentCfg = cfg.cn[2];
15172 if(this.indicatorpos == 'right'){
15178 cls : 'control-label col-form-label',
15182 html : this.fieldLabel
15198 labelCfg = cfg.cn[0];
15199 contentCfg = cfg.cn[1];
15203 if(this.labelWidth > 12){
15204 labelCfg.style = "width: " + this.labelWidth + 'px';
15207 if(this.labelWidth < 13 && this.labelmd == 0){
15208 this.labelmd = this.labelWidth;
15211 if(this.labellg > 0){
15212 labelCfg.cls += ' col-lg-' + this.labellg;
15213 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15216 if(this.labelmd > 0){
15217 labelCfg.cls += ' col-md-' + this.labelmd;
15218 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15221 if(this.labelsm > 0){
15222 labelCfg.cls += ' col-sm-' + this.labelsm;
15223 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15226 if(this.labelxs > 0){
15227 labelCfg.cls += ' col-xs-' + this.labelxs;
15228 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15232 } else if ( this.fieldLabel.length) {
15233 // Roo.log(" label");
15238 //cls : 'input-group-addon',
15239 html : this.fieldLabel
15244 if(this.indicatorpos == 'right'){
15248 //cls : 'input-group-addon',
15249 html : this.fieldLabel
15259 // Roo.log(" no label && no align");
15266 ['xs','sm','md','lg'].map(function(size){
15267 if (settings[size]) {
15268 cfg.cls += ' col-' + size + '-' + settings[size];
15276 _initEventsCalled : false,
15279 initEvents: function()
15281 if (this._initEventsCalled) { // as we call render... prevent looping...
15284 this._initEventsCalled = true;
15287 throw "can not find store for combo";
15290 this.indicator = this.indicatorEl();
15292 this.store = Roo.factory(this.store, Roo.data);
15293 this.store.parent = this;
15295 // if we are building from html. then this element is so complex, that we can not really
15296 // use the rendered HTML.
15297 // so we have to trash and replace the previous code.
15298 if (Roo.XComponent.build_from_html) {
15299 // remove this element....
15300 var e = this.el.dom, k=0;
15301 while (e ) { e = e.previousSibling; ++k;}
15306 this.rendered = false;
15308 this.render(this.parent().getChildContainer(true), k);
15311 if(Roo.isIOS && this.useNativeIOS){
15312 this.initIOSView();
15320 if(Roo.isTouch && this.mobileTouchView){
15321 this.initTouchView();
15326 this.initTickableEvents();
15330 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15332 if(this.hiddenName){
15334 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15336 this.hiddenField.dom.value =
15337 this.hiddenValue !== undefined ? this.hiddenValue :
15338 this.value !== undefined ? this.value : '';
15340 // prevent input submission
15341 this.el.dom.removeAttribute('name');
15342 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15347 // this.el.dom.setAttribute('autocomplete', 'off');
15350 var cls = 'x-combo-list';
15352 //this.list = new Roo.Layer({
15353 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15359 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15360 _this.list.setWidth(lw);
15363 this.list.on('mouseover', this.onViewOver, this);
15364 this.list.on('mousemove', this.onViewMove, this);
15365 this.list.on('scroll', this.onViewScroll, this);
15368 this.list.swallowEvent('mousewheel');
15369 this.assetHeight = 0;
15372 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15373 this.assetHeight += this.header.getHeight();
15376 this.innerList = this.list.createChild({cls:cls+'-inner'});
15377 this.innerList.on('mouseover', this.onViewOver, this);
15378 this.innerList.on('mousemove', this.onViewMove, this);
15379 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15381 if(this.allowBlank && !this.pageSize && !this.disableClear){
15382 this.footer = this.list.createChild({cls:cls+'-ft'});
15383 this.pageTb = new Roo.Toolbar(this.footer);
15387 this.footer = this.list.createChild({cls:cls+'-ft'});
15388 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15389 {pageSize: this.pageSize});
15393 if (this.pageTb && this.allowBlank && !this.disableClear) {
15395 this.pageTb.add(new Roo.Toolbar.Fill(), {
15396 cls: 'x-btn-icon x-btn-clear',
15398 handler: function()
15401 _this.clearValue();
15402 _this.onSelect(false, -1);
15407 this.assetHeight += this.footer.getHeight();
15412 this.tpl = Roo.bootstrap.version == 4 ?
15413 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15414 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15417 this.view = new Roo.View(this.list, this.tpl, {
15418 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15420 //this.view.wrapEl.setDisplayed(false);
15421 this.view.on('click', this.onViewClick, this);
15424 this.store.on('beforeload', this.onBeforeLoad, this);
15425 this.store.on('load', this.onLoad, this);
15426 this.store.on('loadexception', this.onLoadException, this);
15428 if(this.resizable){
15429 this.resizer = new Roo.Resizable(this.list, {
15430 pinned:true, handles:'se'
15432 this.resizer.on('resize', function(r, w, h){
15433 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15434 this.listWidth = w;
15435 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15436 this.restrictHeight();
15438 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15441 if(!this.editable){
15442 this.editable = true;
15443 this.setEditable(false);
15448 if (typeof(this.events.add.listeners) != 'undefined') {
15450 this.addicon = this.wrap.createChild(
15451 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15453 this.addicon.on('click', function(e) {
15454 this.fireEvent('add', this);
15457 if (typeof(this.events.edit.listeners) != 'undefined') {
15459 this.editicon = this.wrap.createChild(
15460 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15461 if (this.addicon) {
15462 this.editicon.setStyle('margin-left', '40px');
15464 this.editicon.on('click', function(e) {
15466 // we fire even if inothing is selected..
15467 this.fireEvent('edit', this, this.lastData );
15473 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15474 "up" : function(e){
15475 this.inKeyMode = true;
15479 "down" : function(e){
15480 if(!this.isExpanded()){
15481 this.onTriggerClick();
15483 this.inKeyMode = true;
15488 "enter" : function(e){
15489 // this.onViewClick();
15493 if(this.fireEvent("specialkey", this, e)){
15494 this.onViewClick(false);
15500 "esc" : function(e){
15504 "tab" : function(e){
15507 if(this.fireEvent("specialkey", this, e)){
15508 this.onViewClick(false);
15516 doRelay : function(foo, bar, hname){
15517 if(hname == 'down' || this.scope.isExpanded()){
15518 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15527 this.queryDelay = Math.max(this.queryDelay || 10,
15528 this.mode == 'local' ? 10 : 250);
15531 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15533 if(this.typeAhead){
15534 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15536 if(this.editable !== false){
15537 this.inputEl().on("keyup", this.onKeyUp, this);
15539 if(this.forceSelection){
15540 this.inputEl().on('blur', this.doForce, this);
15544 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15545 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15549 initTickableEvents: function()
15553 if(this.hiddenName){
15555 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15557 this.hiddenField.dom.value =
15558 this.hiddenValue !== undefined ? this.hiddenValue :
15559 this.value !== undefined ? this.value : '';
15561 // prevent input submission
15562 this.el.dom.removeAttribute('name');
15563 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15568 // this.list = this.el.select('ul.dropdown-menu',true).first();
15570 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15571 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15572 if(this.triggerList){
15573 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15576 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15577 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15579 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15580 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15582 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15583 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15585 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15586 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15587 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15590 this.cancelBtn.hide();
15595 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15596 _this.list.setWidth(lw);
15599 this.list.on('mouseover', this.onViewOver, this);
15600 this.list.on('mousemove', this.onViewMove, this);
15602 this.list.on('scroll', this.onViewScroll, this);
15605 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15606 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15609 this.view = new Roo.View(this.list, this.tpl, {
15614 selectedClass: this.selectedClass
15617 //this.view.wrapEl.setDisplayed(false);
15618 this.view.on('click', this.onViewClick, this);
15622 this.store.on('beforeload', this.onBeforeLoad, this);
15623 this.store.on('load', this.onLoad, this);
15624 this.store.on('loadexception', this.onLoadException, this);
15627 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15628 "up" : function(e){
15629 this.inKeyMode = true;
15633 "down" : function(e){
15634 this.inKeyMode = true;
15638 "enter" : function(e){
15639 if(this.fireEvent("specialkey", this, e)){
15640 this.onViewClick(false);
15646 "esc" : function(e){
15647 this.onTickableFooterButtonClick(e, false, false);
15650 "tab" : function(e){
15651 this.fireEvent("specialkey", this, e);
15653 this.onTickableFooterButtonClick(e, false, false);
15660 doRelay : function(e, fn, key){
15661 if(this.scope.isExpanded()){
15662 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15671 this.queryDelay = Math.max(this.queryDelay || 10,
15672 this.mode == 'local' ? 10 : 250);
15675 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15677 if(this.typeAhead){
15678 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15681 if(this.editable !== false){
15682 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15685 this.indicator = this.indicatorEl();
15687 if(this.indicator){
15688 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15689 this.indicator.hide();
15694 onDestroy : function(){
15696 this.view.setStore(null);
15697 this.view.el.removeAllListeners();
15698 this.view.el.remove();
15699 this.view.purgeListeners();
15702 this.list.dom.innerHTML = '';
15706 this.store.un('beforeload', this.onBeforeLoad, this);
15707 this.store.un('load', this.onLoad, this);
15708 this.store.un('loadexception', this.onLoadException, this);
15710 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15714 fireKey : function(e){
15715 if(e.isNavKeyPress() && !this.list.isVisible()){
15716 this.fireEvent("specialkey", this, e);
15721 onResize: function(w, h){
15722 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15724 // if(typeof w != 'number'){
15725 // // we do not handle it!?!?
15728 // var tw = this.trigger.getWidth();
15729 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15730 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15732 // this.inputEl().setWidth( this.adjustWidth('input', x));
15734 // //this.trigger.setStyle('left', x+'px');
15736 // if(this.list && this.listWidth === undefined){
15737 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15738 // this.list.setWidth(lw);
15739 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15747 * Allow or prevent the user from directly editing the field text. If false is passed,
15748 * the user will only be able to select from the items defined in the dropdown list. This method
15749 * is the runtime equivalent of setting the 'editable' config option at config time.
15750 * @param {Boolean} value True to allow the user to directly edit the field text
15752 setEditable : function(value){
15753 if(value == this.editable){
15756 this.editable = value;
15758 this.inputEl().dom.setAttribute('readOnly', true);
15759 this.inputEl().on('mousedown', this.onTriggerClick, this);
15760 this.inputEl().addClass('x-combo-noedit');
15762 this.inputEl().dom.setAttribute('readOnly', false);
15763 this.inputEl().un('mousedown', this.onTriggerClick, this);
15764 this.inputEl().removeClass('x-combo-noedit');
15770 onBeforeLoad : function(combo,opts){
15771 if(!this.hasFocus){
15775 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15777 this.restrictHeight();
15778 this.selectedIndex = -1;
15782 onLoad : function(){
15784 this.hasQuery = false;
15786 if(!this.hasFocus){
15790 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15791 this.loading.hide();
15794 if(this.store.getCount() > 0){
15797 this.restrictHeight();
15798 if(this.lastQuery == this.allQuery){
15799 if(this.editable && !this.tickable){
15800 this.inputEl().dom.select();
15804 !this.selectByValue(this.value, true) &&
15807 !this.store.lastOptions ||
15808 typeof(this.store.lastOptions.add) == 'undefined' ||
15809 this.store.lastOptions.add != true
15812 this.select(0, true);
15815 if(this.autoFocus){
15818 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15819 this.taTask.delay(this.typeAheadDelay);
15823 this.onEmptyResults();
15829 onLoadException : function()
15831 this.hasQuery = false;
15833 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15834 this.loading.hide();
15837 if(this.tickable && this.editable){
15842 // only causes errors at present
15843 //Roo.log(this.store.reader.jsonData);
15844 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15846 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15852 onTypeAhead : function(){
15853 if(this.store.getCount() > 0){
15854 var r = this.store.getAt(0);
15855 var newValue = r.data[this.displayField];
15856 var len = newValue.length;
15857 var selStart = this.getRawValue().length;
15859 if(selStart != len){
15860 this.setRawValue(newValue);
15861 this.selectText(selStart, newValue.length);
15867 onSelect : function(record, index){
15869 if(this.fireEvent('beforeselect', this, record, index) !== false){
15871 this.setFromData(index > -1 ? record.data : false);
15874 this.fireEvent('select', this, record, index);
15879 * Returns the currently selected field value or empty string if no value is set.
15880 * @return {String} value The selected value
15882 getValue : function()
15884 if(Roo.isIOS && this.useNativeIOS){
15885 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15889 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15892 if(this.valueField){
15893 return typeof this.value != 'undefined' ? this.value : '';
15895 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15899 getRawValue : function()
15901 if(Roo.isIOS && this.useNativeIOS){
15902 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15905 var v = this.inputEl().getValue();
15911 * Clears any text/value currently set in the field
15913 clearValue : function(){
15915 if(this.hiddenField){
15916 this.hiddenField.dom.value = '';
15919 this.setRawValue('');
15920 this.lastSelectionText = '';
15921 this.lastData = false;
15923 var close = this.closeTriggerEl();
15934 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15935 * will be displayed in the field. If the value does not match the data value of an existing item,
15936 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15937 * Otherwise the field will be blank (although the value will still be set).
15938 * @param {String} value The value to match
15940 setValue : function(v)
15942 if(Roo.isIOS && this.useNativeIOS){
15943 this.setIOSValue(v);
15953 if(this.valueField){
15954 var r = this.findRecord(this.valueField, v);
15956 text = r.data[this.displayField];
15957 }else if(this.valueNotFoundText !== undefined){
15958 text = this.valueNotFoundText;
15961 this.lastSelectionText = text;
15962 if(this.hiddenField){
15963 this.hiddenField.dom.value = v;
15965 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15968 var close = this.closeTriggerEl();
15971 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15977 * @property {Object} the last set data for the element
15982 * Sets the value of the field based on a object which is related to the record format for the store.
15983 * @param {Object} value the value to set as. or false on reset?
15985 setFromData : function(o){
15992 var dv = ''; // display value
15993 var vv = ''; // value value..
15995 if (this.displayField) {
15996 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15998 // this is an error condition!!!
15999 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16002 if(this.valueField){
16003 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16006 var close = this.closeTriggerEl();
16009 if(dv.length || vv * 1 > 0){
16011 this.blockFocus=true;
16017 if(this.hiddenField){
16018 this.hiddenField.dom.value = vv;
16020 this.lastSelectionText = dv;
16021 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16025 // no hidden field.. - we store the value in 'value', but still display
16026 // display field!!!!
16027 this.lastSelectionText = dv;
16028 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16035 reset : function(){
16036 // overridden so that last data is reset..
16043 this.setValue(this.originalValue);
16044 //this.clearInvalid();
16045 this.lastData = false;
16047 this.view.clearSelections();
16053 findRecord : function(prop, value){
16055 if(this.store.getCount() > 0){
16056 this.store.each(function(r){
16057 if(r.data[prop] == value){
16067 getName: function()
16069 // returns hidden if it's set..
16070 if (!this.rendered) {return ''};
16071 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16075 onViewMove : function(e, t){
16076 this.inKeyMode = false;
16080 onViewOver : function(e, t){
16081 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16084 var item = this.view.findItemFromChild(t);
16087 var index = this.view.indexOf(item);
16088 this.select(index, false);
16093 onViewClick : function(view, doFocus, el, e)
16095 var index = this.view.getSelectedIndexes()[0];
16097 var r = this.store.getAt(index);
16101 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16108 Roo.each(this.tickItems, function(v,k){
16110 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16112 _this.tickItems.splice(k, 1);
16114 if(typeof(e) == 'undefined' && view == false){
16115 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16127 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16128 this.tickItems.push(r.data);
16131 if(typeof(e) == 'undefined' && view == false){
16132 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16139 this.onSelect(r, index);
16141 if(doFocus !== false && !this.blockFocus){
16142 this.inputEl().focus();
16147 restrictHeight : function(){
16148 //this.innerList.dom.style.height = '';
16149 //var inner = this.innerList.dom;
16150 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16151 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16152 //this.list.beginUpdate();
16153 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16154 this.list.alignTo(this.inputEl(), this.listAlign);
16155 this.list.alignTo(this.inputEl(), this.listAlign);
16156 //this.list.endUpdate();
16160 onEmptyResults : function(){
16162 if(this.tickable && this.editable){
16163 this.hasFocus = false;
16164 this.restrictHeight();
16172 * Returns true if the dropdown list is expanded, else false.
16174 isExpanded : function(){
16175 return this.list.isVisible();
16179 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16180 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16181 * @param {String} value The data value of the item to select
16182 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16183 * selected item if it is not currently in view (defaults to true)
16184 * @return {Boolean} True if the value matched an item in the list, else false
16186 selectByValue : function(v, scrollIntoView){
16187 if(v !== undefined && v !== null){
16188 var r = this.findRecord(this.valueField || this.displayField, v);
16190 this.select(this.store.indexOf(r), scrollIntoView);
16198 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16199 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16200 * @param {Number} index The zero-based index of the list item to select
16201 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16202 * selected item if it is not currently in view (defaults to true)
16204 select : function(index, scrollIntoView){
16205 this.selectedIndex = index;
16206 this.view.select(index);
16207 if(scrollIntoView !== false){
16208 var el = this.view.getNode(index);
16210 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16213 this.list.scrollChildIntoView(el, false);
16219 selectNext : function(){
16220 var ct = this.store.getCount();
16222 if(this.selectedIndex == -1){
16224 }else if(this.selectedIndex < ct-1){
16225 this.select(this.selectedIndex+1);
16231 selectPrev : function(){
16232 var ct = this.store.getCount();
16234 if(this.selectedIndex == -1){
16236 }else if(this.selectedIndex != 0){
16237 this.select(this.selectedIndex-1);
16243 onKeyUp : function(e){
16244 if(this.editable !== false && !e.isSpecialKey()){
16245 this.lastKey = e.getKey();
16246 this.dqTask.delay(this.queryDelay);
16251 validateBlur : function(){
16252 return !this.list || !this.list.isVisible();
16256 initQuery : function(){
16258 var v = this.getRawValue();
16260 if(this.tickable && this.editable){
16261 v = this.tickableInputEl().getValue();
16268 doForce : function(){
16269 if(this.inputEl().dom.value.length > 0){
16270 this.inputEl().dom.value =
16271 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16277 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16278 * query allowing the query action to be canceled if needed.
16279 * @param {String} query The SQL query to execute
16280 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16281 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16282 * saved in the current store (defaults to false)
16284 doQuery : function(q, forceAll){
16286 if(q === undefined || q === null){
16291 forceAll: forceAll,
16295 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16300 forceAll = qe.forceAll;
16301 if(forceAll === true || (q.length >= this.minChars)){
16303 this.hasQuery = true;
16305 if(this.lastQuery != q || this.alwaysQuery){
16306 this.lastQuery = q;
16307 if(this.mode == 'local'){
16308 this.selectedIndex = -1;
16310 this.store.clearFilter();
16313 if(this.specialFilter){
16314 this.fireEvent('specialfilter', this);
16319 this.store.filter(this.displayField, q);
16322 this.store.fireEvent("datachanged", this.store);
16329 this.store.baseParams[this.queryParam] = q;
16331 var options = {params : this.getParams(q)};
16334 options.add = true;
16335 options.params.start = this.page * this.pageSize;
16338 this.store.load(options);
16341 * this code will make the page width larger, at the beginning, the list not align correctly,
16342 * we should expand the list on onLoad
16343 * so command out it
16348 this.selectedIndex = -1;
16353 this.loadNext = false;
16357 getParams : function(q){
16359 //p[this.queryParam] = q;
16363 p.limit = this.pageSize;
16369 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16371 collapse : function(){
16372 if(!this.isExpanded()){
16378 this.hasFocus = false;
16382 this.cancelBtn.hide();
16383 this.trigger.show();
16386 this.tickableInputEl().dom.value = '';
16387 this.tickableInputEl().blur();
16392 Roo.get(document).un('mousedown', this.collapseIf, this);
16393 Roo.get(document).un('mousewheel', this.collapseIf, this);
16394 if (!this.editable) {
16395 Roo.get(document).un('keydown', this.listKeyPress, this);
16397 this.fireEvent('collapse', this);
16403 collapseIf : function(e){
16404 var in_combo = e.within(this.el);
16405 var in_list = e.within(this.list);
16406 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16408 if (in_combo || in_list || is_list) {
16409 //e.stopPropagation();
16414 this.onTickableFooterButtonClick(e, false, false);
16422 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16424 expand : function(){
16426 if(this.isExpanded() || !this.hasFocus){
16430 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16431 this.list.setWidth(lw);
16437 this.restrictHeight();
16441 this.tickItems = Roo.apply([], this.item);
16444 this.cancelBtn.show();
16445 this.trigger.hide();
16448 this.tickableInputEl().focus();
16453 Roo.get(document).on('mousedown', this.collapseIf, this);
16454 Roo.get(document).on('mousewheel', this.collapseIf, this);
16455 if (!this.editable) {
16456 Roo.get(document).on('keydown', this.listKeyPress, this);
16459 this.fireEvent('expand', this);
16463 // Implements the default empty TriggerField.onTriggerClick function
16464 onTriggerClick : function(e)
16466 Roo.log('trigger click');
16468 if(this.disabled || !this.triggerList){
16473 this.loadNext = false;
16475 if(this.isExpanded()){
16477 if (!this.blockFocus) {
16478 this.inputEl().focus();
16482 this.hasFocus = true;
16483 if(this.triggerAction == 'all') {
16484 this.doQuery(this.allQuery, true);
16486 this.doQuery(this.getRawValue());
16488 if (!this.blockFocus) {
16489 this.inputEl().focus();
16494 onTickableTriggerClick : function(e)
16501 this.loadNext = false;
16502 this.hasFocus = true;
16504 if(this.triggerAction == 'all') {
16505 this.doQuery(this.allQuery, true);
16507 this.doQuery(this.getRawValue());
16511 onSearchFieldClick : function(e)
16513 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16514 this.onTickableFooterButtonClick(e, false, false);
16518 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16523 this.loadNext = false;
16524 this.hasFocus = true;
16526 if(this.triggerAction == 'all') {
16527 this.doQuery(this.allQuery, true);
16529 this.doQuery(this.getRawValue());
16533 listKeyPress : function(e)
16535 //Roo.log('listkeypress');
16536 // scroll to first matching element based on key pres..
16537 if (e.isSpecialKey()) {
16540 var k = String.fromCharCode(e.getKey()).toUpperCase();
16543 var csel = this.view.getSelectedNodes();
16544 var cselitem = false;
16546 var ix = this.view.indexOf(csel[0]);
16547 cselitem = this.store.getAt(ix);
16548 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16554 this.store.each(function(v) {
16556 // start at existing selection.
16557 if (cselitem.id == v.id) {
16563 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16564 match = this.store.indexOf(v);
16570 if (match === false) {
16571 return true; // no more action?
16574 this.view.select(match);
16575 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16576 sn.scrollIntoView(sn.dom.parentNode, false);
16579 onViewScroll : function(e, t){
16581 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){
16585 this.hasQuery = true;
16587 this.loading = this.list.select('.loading', true).first();
16589 if(this.loading === null){
16590 this.list.createChild({
16592 cls: 'loading roo-select2-more-results roo-select2-active',
16593 html: 'Loading more results...'
16596 this.loading = this.list.select('.loading', true).first();
16598 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16600 this.loading.hide();
16603 this.loading.show();
16608 this.loadNext = true;
16610 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16615 addItem : function(o)
16617 var dv = ''; // display value
16619 if (this.displayField) {
16620 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16622 // this is an error condition!!!
16623 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16630 var choice = this.choices.createChild({
16632 cls: 'roo-select2-search-choice',
16641 cls: 'roo-select2-search-choice-close fa fa-times',
16646 }, this.searchField);
16648 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16650 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16658 this.inputEl().dom.value = '';
16663 onRemoveItem : function(e, _self, o)
16665 e.preventDefault();
16667 this.lastItem = Roo.apply([], this.item);
16669 var index = this.item.indexOf(o.data) * 1;
16672 Roo.log('not this item?!');
16676 this.item.splice(index, 1);
16681 this.fireEvent('remove', this, e);
16687 syncValue : function()
16689 if(!this.item.length){
16696 Roo.each(this.item, function(i){
16697 if(_this.valueField){
16698 value.push(i[_this.valueField]);
16705 this.value = value.join(',');
16707 if(this.hiddenField){
16708 this.hiddenField.dom.value = this.value;
16711 this.store.fireEvent("datachanged", this.store);
16716 clearItem : function()
16718 if(!this.multiple){
16724 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16732 if(this.tickable && !Roo.isTouch){
16733 this.view.refresh();
16737 inputEl: function ()
16739 if(Roo.isIOS && this.useNativeIOS){
16740 return this.el.select('select.roo-ios-select', true).first();
16743 if(Roo.isTouch && this.mobileTouchView){
16744 return this.el.select('input.form-control',true).first();
16748 return this.searchField;
16751 return this.el.select('input.form-control',true).first();
16754 onTickableFooterButtonClick : function(e, btn, el)
16756 e.preventDefault();
16758 this.lastItem = Roo.apply([], this.item);
16760 if(btn && btn.name == 'cancel'){
16761 this.tickItems = Roo.apply([], this.item);
16770 Roo.each(this.tickItems, function(o){
16778 validate : function()
16780 if(this.getVisibilityEl().hasClass('hidden')){
16784 var v = this.getRawValue();
16787 v = this.getValue();
16790 if(this.disabled || this.allowBlank || v.length){
16795 this.markInvalid();
16799 tickableInputEl : function()
16801 if(!this.tickable || !this.editable){
16802 return this.inputEl();
16805 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16809 getAutoCreateTouchView : function()
16814 cls: 'form-group' //input-group
16820 type : this.inputType,
16821 cls : 'form-control x-combo-noedit',
16822 autocomplete: 'new-password',
16823 placeholder : this.placeholder || '',
16828 input.name = this.name;
16832 input.cls += ' input-' + this.size;
16835 if (this.disabled) {
16836 input.disabled = true;
16847 inputblock.cls += ' input-group';
16849 inputblock.cn.unshift({
16851 cls : 'input-group-addon input-group-prepend input-group-text',
16856 if(this.removable && !this.multiple){
16857 inputblock.cls += ' roo-removable';
16859 inputblock.cn.push({
16862 cls : 'roo-combo-removable-btn close'
16866 if(this.hasFeedback && !this.allowBlank){
16868 inputblock.cls += ' has-feedback';
16870 inputblock.cn.push({
16872 cls: 'glyphicon form-control-feedback'
16879 inputblock.cls += (this.before) ? '' : ' input-group';
16881 inputblock.cn.push({
16883 cls : 'input-group-addon input-group-append input-group-text',
16889 var ibwrap = inputblock;
16894 cls: 'roo-select2-choices',
16898 cls: 'roo-select2-search-field',
16911 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16916 cls: 'form-hidden-field'
16922 if(!this.multiple && this.showToggleBtn){
16928 if (this.caret != false) {
16931 cls: 'fa fa-' + this.caret
16938 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16940 Roo.bootstrap.version == 3 ? caret : '',
16943 cls: 'combobox-clear',
16957 combobox.cls += ' roo-select2-container-multi';
16960 var align = this.labelAlign || this.parentLabelAlign();
16962 if (align ==='left' && this.fieldLabel.length) {
16967 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16968 tooltip : 'This field is required'
16972 cls : 'control-label col-form-label',
16973 html : this.fieldLabel
16984 var labelCfg = cfg.cn[1];
16985 var contentCfg = cfg.cn[2];
16988 if(this.indicatorpos == 'right'){
16993 cls : 'control-label col-form-label',
16997 html : this.fieldLabel
17001 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17002 tooltip : 'This field is required'
17015 labelCfg = cfg.cn[0];
17016 contentCfg = cfg.cn[1];
17021 if(this.labelWidth > 12){
17022 labelCfg.style = "width: " + this.labelWidth + 'px';
17025 if(this.labelWidth < 13 && this.labelmd == 0){
17026 this.labelmd = this.labelWidth;
17029 if(this.labellg > 0){
17030 labelCfg.cls += ' col-lg-' + this.labellg;
17031 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17034 if(this.labelmd > 0){
17035 labelCfg.cls += ' col-md-' + this.labelmd;
17036 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17039 if(this.labelsm > 0){
17040 labelCfg.cls += ' col-sm-' + this.labelsm;
17041 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17044 if(this.labelxs > 0){
17045 labelCfg.cls += ' col-xs-' + this.labelxs;
17046 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17050 } else if ( this.fieldLabel.length) {
17054 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17055 tooltip : 'This field is required'
17059 cls : 'control-label',
17060 html : this.fieldLabel
17071 if(this.indicatorpos == 'right'){
17075 cls : 'control-label',
17076 html : this.fieldLabel,
17080 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17081 tooltip : 'This field is required'
17098 var settings = this;
17100 ['xs','sm','md','lg'].map(function(size){
17101 if (settings[size]) {
17102 cfg.cls += ' col-' + size + '-' + settings[size];
17109 initTouchView : function()
17111 this.renderTouchView();
17113 this.touchViewEl.on('scroll', function(){
17114 this.el.dom.scrollTop = 0;
17117 this.originalValue = this.getValue();
17119 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17121 this.inputEl().on("click", this.showTouchView, this);
17122 if (this.triggerEl) {
17123 this.triggerEl.on("click", this.showTouchView, this);
17127 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17128 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17130 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17132 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17133 this.store.on('load', this.onTouchViewLoad, this);
17134 this.store.on('loadexception', this.onTouchViewLoadException, this);
17136 if(this.hiddenName){
17138 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17140 this.hiddenField.dom.value =
17141 this.hiddenValue !== undefined ? this.hiddenValue :
17142 this.value !== undefined ? this.value : '';
17144 this.el.dom.removeAttribute('name');
17145 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17149 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17150 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17153 if(this.removable && !this.multiple){
17154 var close = this.closeTriggerEl();
17156 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17157 close.on('click', this.removeBtnClick, this, close);
17161 * fix the bug in Safari iOS8
17163 this.inputEl().on("focus", function(e){
17164 document.activeElement.blur();
17167 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17174 renderTouchView : function()
17176 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17177 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17179 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17180 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17182 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17183 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17184 this.touchViewBodyEl.setStyle('overflow', 'auto');
17186 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17187 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17189 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17190 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17194 showTouchView : function()
17200 this.touchViewHeaderEl.hide();
17202 if(this.modalTitle.length){
17203 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17204 this.touchViewHeaderEl.show();
17207 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17208 this.touchViewEl.show();
17210 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17212 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17213 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17215 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17217 if(this.modalTitle.length){
17218 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17221 this.touchViewBodyEl.setHeight(bodyHeight);
17225 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17227 this.touchViewEl.addClass('in');
17230 if(this._touchViewMask){
17231 Roo.get(document.body).addClass("x-body-masked");
17232 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17233 this._touchViewMask.setStyle('z-index', 10000);
17234 this._touchViewMask.addClass('show');
17237 this.doTouchViewQuery();
17241 hideTouchView : function()
17243 this.touchViewEl.removeClass('in');
17247 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17249 this.touchViewEl.setStyle('display', 'none');
17252 if(this._touchViewMask){
17253 this._touchViewMask.removeClass('show');
17254 Roo.get(document.body).removeClass("x-body-masked");
17258 setTouchViewValue : function()
17265 Roo.each(this.tickItems, function(o){
17270 this.hideTouchView();
17273 doTouchViewQuery : function()
17282 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17286 if(!this.alwaysQuery || this.mode == 'local'){
17287 this.onTouchViewLoad();
17294 onTouchViewBeforeLoad : function(combo,opts)
17300 onTouchViewLoad : function()
17302 if(this.store.getCount() < 1){
17303 this.onTouchViewEmptyResults();
17307 this.clearTouchView();
17309 var rawValue = this.getRawValue();
17311 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17313 this.tickItems = [];
17315 this.store.data.each(function(d, rowIndex){
17316 var row = this.touchViewListGroup.createChild(template);
17318 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17319 row.addClass(d.data.cls);
17322 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17325 html : d.data[this.displayField]
17328 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17329 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17332 row.removeClass('selected');
17333 if(!this.multiple && this.valueField &&
17334 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17337 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17338 row.addClass('selected');
17341 if(this.multiple && this.valueField &&
17342 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17346 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17347 this.tickItems.push(d.data);
17350 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17354 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17356 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17358 if(this.modalTitle.length){
17359 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17362 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17364 if(this.mobile_restrict_height && listHeight < bodyHeight){
17365 this.touchViewBodyEl.setHeight(listHeight);
17370 if(firstChecked && listHeight > bodyHeight){
17371 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17376 onTouchViewLoadException : function()
17378 this.hideTouchView();
17381 onTouchViewEmptyResults : function()
17383 this.clearTouchView();
17385 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17387 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17391 clearTouchView : function()
17393 this.touchViewListGroup.dom.innerHTML = '';
17396 onTouchViewClick : function(e, el, o)
17398 e.preventDefault();
17401 var rowIndex = o.rowIndex;
17403 var r = this.store.getAt(rowIndex);
17405 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17407 if(!this.multiple){
17408 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17409 c.dom.removeAttribute('checked');
17412 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17414 this.setFromData(r.data);
17416 var close = this.closeTriggerEl();
17422 this.hideTouchView();
17424 this.fireEvent('select', this, r, rowIndex);
17429 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17430 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17431 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17435 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17436 this.addItem(r.data);
17437 this.tickItems.push(r.data);
17441 getAutoCreateNativeIOS : function()
17444 cls: 'form-group' //input-group,
17449 cls : 'roo-ios-select'
17453 combobox.name = this.name;
17456 if (this.disabled) {
17457 combobox.disabled = true;
17460 var settings = this;
17462 ['xs','sm','md','lg'].map(function(size){
17463 if (settings[size]) {
17464 cfg.cls += ' col-' + size + '-' + settings[size];
17474 initIOSView : function()
17476 this.store.on('load', this.onIOSViewLoad, this);
17481 onIOSViewLoad : function()
17483 if(this.store.getCount() < 1){
17487 this.clearIOSView();
17489 if(this.allowBlank) {
17491 var default_text = '-- SELECT --';
17493 if(this.placeholder.length){
17494 default_text = this.placeholder;
17497 if(this.emptyTitle.length){
17498 default_text += ' - ' + this.emptyTitle + ' -';
17501 var opt = this.inputEl().createChild({
17504 html : default_text
17508 o[this.valueField] = 0;
17509 o[this.displayField] = default_text;
17511 this.ios_options.push({
17518 this.store.data.each(function(d, rowIndex){
17522 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17523 html = d.data[this.displayField];
17528 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17529 value = d.data[this.valueField];
17538 if(this.value == d.data[this.valueField]){
17539 option['selected'] = true;
17542 var opt = this.inputEl().createChild(option);
17544 this.ios_options.push({
17551 this.inputEl().on('change', function(){
17552 this.fireEvent('select', this);
17557 clearIOSView: function()
17559 this.inputEl().dom.innerHTML = '';
17561 this.ios_options = [];
17564 setIOSValue: function(v)
17568 if(!this.ios_options){
17572 Roo.each(this.ios_options, function(opts){
17574 opts.el.dom.removeAttribute('selected');
17576 if(opts.data[this.valueField] != v){
17580 opts.el.dom.setAttribute('selected', true);
17586 * @cfg {Boolean} grow
17590 * @cfg {Number} growMin
17594 * @cfg {Number} growMax
17603 Roo.apply(Roo.bootstrap.ComboBox, {
17607 cls: 'modal-header',
17629 cls: 'list-group-item',
17633 cls: 'roo-combobox-list-group-item-value'
17637 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17651 listItemCheckbox : {
17653 cls: 'list-group-item',
17657 cls: 'roo-combobox-list-group-item-value'
17661 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17677 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17682 cls: 'modal-footer',
17690 cls: 'col-xs-6 text-left',
17693 cls: 'btn btn-danger roo-touch-view-cancel',
17699 cls: 'col-xs-6 text-right',
17702 cls: 'btn btn-success roo-touch-view-ok',
17713 Roo.apply(Roo.bootstrap.ComboBox, {
17715 touchViewTemplate : {
17717 cls: 'modal fade roo-combobox-touch-view',
17721 cls: 'modal-dialog',
17722 style : 'position:fixed', // we have to fix position....
17726 cls: 'modal-content',
17728 Roo.bootstrap.ComboBox.header,
17729 Roo.bootstrap.ComboBox.body,
17730 Roo.bootstrap.ComboBox.footer
17739 * Ext JS Library 1.1.1
17740 * Copyright(c) 2006-2007, Ext JS, LLC.
17742 * Originally Released Under LGPL - original licence link has changed is not relivant.
17745 * <script type="text/javascript">
17750 * @extends Roo.util.Observable
17751 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17752 * This class also supports single and multi selection modes. <br>
17753 * Create a data model bound view:
17755 var store = new Roo.data.Store(...);
17757 var view = new Roo.View({
17759 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17761 singleSelect: true,
17762 selectedClass: "ydataview-selected",
17766 // listen for node click?
17767 view.on("click", function(vw, index, node, e){
17768 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17772 dataModel.load("foobar.xml");
17774 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17776 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17777 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17779 * Note: old style constructor is still suported (container, template, config)
17782 * Create a new View
17783 * @param {Object} config The config object
17786 Roo.View = function(config, depreciated_tpl, depreciated_config){
17788 this.parent = false;
17790 if (typeof(depreciated_tpl) == 'undefined') {
17791 // new way.. - universal constructor.
17792 Roo.apply(this, config);
17793 this.el = Roo.get(this.el);
17796 this.el = Roo.get(config);
17797 this.tpl = depreciated_tpl;
17798 Roo.apply(this, depreciated_config);
17800 this.wrapEl = this.el.wrap().wrap();
17801 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17804 if(typeof(this.tpl) == "string"){
17805 this.tpl = new Roo.Template(this.tpl);
17807 // support xtype ctors..
17808 this.tpl = new Roo.factory(this.tpl, Roo);
17812 this.tpl.compile();
17817 * @event beforeclick
17818 * Fires before a click is processed. Returns false to cancel the default action.
17819 * @param {Roo.View} this
17820 * @param {Number} index The index of the target node
17821 * @param {HTMLElement} node The target node
17822 * @param {Roo.EventObject} e The raw event object
17824 "beforeclick" : true,
17827 * Fires when a template node is clicked.
17828 * @param {Roo.View} this
17829 * @param {Number} index The index of the target node
17830 * @param {HTMLElement} node The target node
17831 * @param {Roo.EventObject} e The raw event object
17836 * Fires when a template node is double clicked.
17837 * @param {Roo.View} this
17838 * @param {Number} index The index of the target node
17839 * @param {HTMLElement} node The target node
17840 * @param {Roo.EventObject} e The raw event object
17844 * @event contextmenu
17845 * Fires when a template node is right clicked.
17846 * @param {Roo.View} this
17847 * @param {Number} index The index of the target node
17848 * @param {HTMLElement} node The target node
17849 * @param {Roo.EventObject} e The raw event object
17851 "contextmenu" : true,
17853 * @event selectionchange
17854 * Fires when the selected nodes change.
17855 * @param {Roo.View} this
17856 * @param {Array} selections Array of the selected nodes
17858 "selectionchange" : true,
17861 * @event beforeselect
17862 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17863 * @param {Roo.View} this
17864 * @param {HTMLElement} node The node to be selected
17865 * @param {Array} selections Array of currently selected nodes
17867 "beforeselect" : true,
17869 * @event preparedata
17870 * Fires on every row to render, to allow you to change the data.
17871 * @param {Roo.View} this
17872 * @param {Object} data to be rendered (change this)
17874 "preparedata" : true
17882 "click": this.onClick,
17883 "dblclick": this.onDblClick,
17884 "contextmenu": this.onContextMenu,
17888 this.selections = [];
17890 this.cmp = new Roo.CompositeElementLite([]);
17892 this.store = Roo.factory(this.store, Roo.data);
17893 this.setStore(this.store, true);
17896 if ( this.footer && this.footer.xtype) {
17898 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17900 this.footer.dataSource = this.store;
17901 this.footer.container = fctr;
17902 this.footer = Roo.factory(this.footer, Roo);
17903 fctr.insertFirst(this.el);
17905 // this is a bit insane - as the paging toolbar seems to detach the el..
17906 // dom.parentNode.parentNode.parentNode
17907 // they get detached?
17911 Roo.View.superclass.constructor.call(this);
17916 Roo.extend(Roo.View, Roo.util.Observable, {
17919 * @cfg {Roo.data.Store} store Data store to load data from.
17924 * @cfg {String|Roo.Element} el The container element.
17929 * @cfg {String|Roo.Template} tpl The template used by this View
17933 * @cfg {String} dataName the named area of the template to use as the data area
17934 * Works with domtemplates roo-name="name"
17938 * @cfg {String} selectedClass The css class to add to selected nodes
17940 selectedClass : "x-view-selected",
17942 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17947 * @cfg {String} text to display on mask (default Loading)
17951 * @cfg {Boolean} multiSelect Allow multiple selection
17953 multiSelect : false,
17955 * @cfg {Boolean} singleSelect Allow single selection
17957 singleSelect: false,
17960 * @cfg {Boolean} toggleSelect - selecting
17962 toggleSelect : false,
17965 * @cfg {Boolean} tickable - selecting
17970 * Returns the element this view is bound to.
17971 * @return {Roo.Element}
17973 getEl : function(){
17974 return this.wrapEl;
17980 * Refreshes the view. - called by datachanged on the store. - do not call directly.
17982 refresh : function(){
17983 //Roo.log('refresh');
17986 // if we are using something like 'domtemplate', then
17987 // the what gets used is:
17988 // t.applySubtemplate(NAME, data, wrapping data..)
17989 // the outer template then get' applied with
17990 // the store 'extra data'
17991 // and the body get's added to the
17992 // roo-name="data" node?
17993 // <span class='roo-tpl-{name}'></span> ?????
17997 this.clearSelections();
17998 this.el.update("");
18000 var records = this.store.getRange();
18001 if(records.length < 1) {
18003 // is this valid?? = should it render a template??
18005 this.el.update(this.emptyText);
18009 if (this.dataName) {
18010 this.el.update(t.apply(this.store.meta)); //????
18011 el = this.el.child('.roo-tpl-' + this.dataName);
18014 for(var i = 0, len = records.length; i < len; i++){
18015 var data = this.prepareData(records[i].data, i, records[i]);
18016 this.fireEvent("preparedata", this, data, i, records[i]);
18018 var d = Roo.apply({}, data);
18021 Roo.apply(d, {'roo-id' : Roo.id()});
18025 Roo.each(this.parent.item, function(item){
18026 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18029 Roo.apply(d, {'roo-data-checked' : 'checked'});
18033 html[html.length] = Roo.util.Format.trim(
18035 t.applySubtemplate(this.dataName, d, this.store.meta) :
18042 el.update(html.join(""));
18043 this.nodes = el.dom.childNodes;
18044 this.updateIndexes(0);
18049 * Function to override to reformat the data that is sent to
18050 * the template for each node.
18051 * DEPRICATED - use the preparedata event handler.
18052 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18053 * a JSON object for an UpdateManager bound view).
18055 prepareData : function(data, index, record)
18057 this.fireEvent("preparedata", this, data, index, record);
18061 onUpdate : function(ds, record){
18062 // Roo.log('on update');
18063 this.clearSelections();
18064 var index = this.store.indexOf(record);
18065 var n = this.nodes[index];
18066 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18067 n.parentNode.removeChild(n);
18068 this.updateIndexes(index, index);
18074 onAdd : function(ds, records, index)
18076 //Roo.log(['on Add', ds, records, index] );
18077 this.clearSelections();
18078 if(this.nodes.length == 0){
18082 var n = this.nodes[index];
18083 for(var i = 0, len = records.length; i < len; i++){
18084 var d = this.prepareData(records[i].data, i, records[i]);
18086 this.tpl.insertBefore(n, d);
18089 this.tpl.append(this.el, d);
18092 this.updateIndexes(index);
18095 onRemove : function(ds, record, index){
18096 // Roo.log('onRemove');
18097 this.clearSelections();
18098 var el = this.dataName ?
18099 this.el.child('.roo-tpl-' + this.dataName) :
18102 el.dom.removeChild(this.nodes[index]);
18103 this.updateIndexes(index);
18107 * Refresh an individual node.
18108 * @param {Number} index
18110 refreshNode : function(index){
18111 this.onUpdate(this.store, this.store.getAt(index));
18114 updateIndexes : function(startIndex, endIndex){
18115 var ns = this.nodes;
18116 startIndex = startIndex || 0;
18117 endIndex = endIndex || ns.length - 1;
18118 for(var i = startIndex; i <= endIndex; i++){
18119 ns[i].nodeIndex = i;
18124 * Changes the data store this view uses and refresh the view.
18125 * @param {Store} store
18127 setStore : function(store, initial){
18128 if(!initial && this.store){
18129 this.store.un("datachanged", this.refresh);
18130 this.store.un("add", this.onAdd);
18131 this.store.un("remove", this.onRemove);
18132 this.store.un("update", this.onUpdate);
18133 this.store.un("clear", this.refresh);
18134 this.store.un("beforeload", this.onBeforeLoad);
18135 this.store.un("load", this.onLoad);
18136 this.store.un("loadexception", this.onLoad);
18140 store.on("datachanged", this.refresh, this);
18141 store.on("add", this.onAdd, this);
18142 store.on("remove", this.onRemove, this);
18143 store.on("update", this.onUpdate, this);
18144 store.on("clear", this.refresh, this);
18145 store.on("beforeload", this.onBeforeLoad, this);
18146 store.on("load", this.onLoad, this);
18147 store.on("loadexception", this.onLoad, this);
18155 * onbeforeLoad - masks the loading area.
18158 onBeforeLoad : function(store,opts)
18160 //Roo.log('onBeforeLoad');
18162 this.el.update("");
18164 this.el.mask(this.mask ? this.mask : "Loading" );
18166 onLoad : function ()
18173 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18174 * @param {HTMLElement} node
18175 * @return {HTMLElement} The template node
18177 findItemFromChild : function(node){
18178 var el = this.dataName ?
18179 this.el.child('.roo-tpl-' + this.dataName,true) :
18182 if(!node || node.parentNode == el){
18185 var p = node.parentNode;
18186 while(p && p != el){
18187 if(p.parentNode == el){
18196 onClick : function(e){
18197 var item = this.findItemFromChild(e.getTarget());
18199 var index = this.indexOf(item);
18200 if(this.onItemClick(item, index, e) !== false){
18201 this.fireEvent("click", this, index, item, e);
18204 this.clearSelections();
18209 onContextMenu : function(e){
18210 var item = this.findItemFromChild(e.getTarget());
18212 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18217 onDblClick : function(e){
18218 var item = this.findItemFromChild(e.getTarget());
18220 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18224 onItemClick : function(item, index, e)
18226 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18229 if (this.toggleSelect) {
18230 var m = this.isSelected(item) ? 'unselect' : 'select';
18233 _t[m](item, true, false);
18236 if(this.multiSelect || this.singleSelect){
18237 if(this.multiSelect && e.shiftKey && this.lastSelection){
18238 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18240 this.select(item, this.multiSelect && e.ctrlKey);
18241 this.lastSelection = item;
18244 if(!this.tickable){
18245 e.preventDefault();
18253 * Get the number of selected nodes.
18256 getSelectionCount : function(){
18257 return this.selections.length;
18261 * Get the currently selected nodes.
18262 * @return {Array} An array of HTMLElements
18264 getSelectedNodes : function(){
18265 return this.selections;
18269 * Get the indexes of the selected nodes.
18272 getSelectedIndexes : function(){
18273 var indexes = [], s = this.selections;
18274 for(var i = 0, len = s.length; i < len; i++){
18275 indexes.push(s[i].nodeIndex);
18281 * Clear all selections
18282 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18284 clearSelections : function(suppressEvent){
18285 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18286 this.cmp.elements = this.selections;
18287 this.cmp.removeClass(this.selectedClass);
18288 this.selections = [];
18289 if(!suppressEvent){
18290 this.fireEvent("selectionchange", this, this.selections);
18296 * Returns true if the passed node is selected
18297 * @param {HTMLElement/Number} node The node or node index
18298 * @return {Boolean}
18300 isSelected : function(node){
18301 var s = this.selections;
18305 node = this.getNode(node);
18306 return s.indexOf(node) !== -1;
18311 * @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
18312 * @param {Boolean} keepExisting (optional) true to keep existing selections
18313 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18315 select : function(nodeInfo, keepExisting, suppressEvent){
18316 if(nodeInfo instanceof Array){
18318 this.clearSelections(true);
18320 for(var i = 0, len = nodeInfo.length; i < len; i++){
18321 this.select(nodeInfo[i], true, true);
18325 var node = this.getNode(nodeInfo);
18326 if(!node || this.isSelected(node)){
18327 return; // already selected.
18330 this.clearSelections(true);
18333 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18334 Roo.fly(node).addClass(this.selectedClass);
18335 this.selections.push(node);
18336 if(!suppressEvent){
18337 this.fireEvent("selectionchange", this, this.selections);
18345 * @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
18346 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18347 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18349 unselect : function(nodeInfo, keepExisting, suppressEvent)
18351 if(nodeInfo instanceof Array){
18352 Roo.each(this.selections, function(s) {
18353 this.unselect(s, nodeInfo);
18357 var node = this.getNode(nodeInfo);
18358 if(!node || !this.isSelected(node)){
18359 //Roo.log("not selected");
18360 return; // not selected.
18364 Roo.each(this.selections, function(s) {
18366 Roo.fly(node).removeClass(this.selectedClass);
18373 this.selections= ns;
18374 this.fireEvent("selectionchange", this, this.selections);
18378 * Gets a template node.
18379 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18380 * @return {HTMLElement} The node or null if it wasn't found
18382 getNode : function(nodeInfo){
18383 if(typeof nodeInfo == "string"){
18384 return document.getElementById(nodeInfo);
18385 }else if(typeof nodeInfo == "number"){
18386 return this.nodes[nodeInfo];
18392 * Gets a range template nodes.
18393 * @param {Number} startIndex
18394 * @param {Number} endIndex
18395 * @return {Array} An array of nodes
18397 getNodes : function(start, end){
18398 var ns = this.nodes;
18399 start = start || 0;
18400 end = typeof end == "undefined" ? ns.length - 1 : end;
18403 for(var i = start; i <= end; i++){
18407 for(var i = start; i >= end; i--){
18415 * Finds the index of the passed node
18416 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18417 * @return {Number} The index of the node or -1
18419 indexOf : function(node){
18420 node = this.getNode(node);
18421 if(typeof node.nodeIndex == "number"){
18422 return node.nodeIndex;
18424 var ns = this.nodes;
18425 for(var i = 0, len = ns.length; i < len; i++){
18436 * based on jquery fullcalendar
18440 Roo.bootstrap = Roo.bootstrap || {};
18442 * @class Roo.bootstrap.Calendar
18443 * @extends Roo.bootstrap.Component
18444 * Bootstrap Calendar class
18445 * @cfg {Boolean} loadMask (true|false) default false
18446 * @cfg {Object} header generate the user specific header of the calendar, default false
18449 * Create a new Container
18450 * @param {Object} config The config object
18455 Roo.bootstrap.Calendar = function(config){
18456 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18460 * Fires when a date is selected
18461 * @param {DatePicker} this
18462 * @param {Date} date The selected date
18466 * @event monthchange
18467 * Fires when the displayed month changes
18468 * @param {DatePicker} this
18469 * @param {Date} date The selected month
18471 'monthchange': true,
18473 * @event evententer
18474 * Fires when mouse over an event
18475 * @param {Calendar} this
18476 * @param {event} Event
18478 'evententer': true,
18480 * @event eventleave
18481 * Fires when the mouse leaves an
18482 * @param {Calendar} this
18485 'eventleave': true,
18487 * @event eventclick
18488 * Fires when the mouse click an
18489 * @param {Calendar} this
18498 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18501 * @cfg {Number} startDay
18502 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18510 getAutoCreate : function(){
18513 var fc_button = function(name, corner, style, content ) {
18514 return Roo.apply({},{
18516 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18518 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18521 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18532 style : 'width:100%',
18539 cls : 'fc-header-left',
18541 fc_button('prev', 'left', 'arrow', '‹' ),
18542 fc_button('next', 'right', 'arrow', '›' ),
18543 { tag: 'span', cls: 'fc-header-space' },
18544 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18552 cls : 'fc-header-center',
18556 cls: 'fc-header-title',
18559 html : 'month / year'
18567 cls : 'fc-header-right',
18569 /* fc_button('month', 'left', '', 'month' ),
18570 fc_button('week', '', '', 'week' ),
18571 fc_button('day', 'right', '', 'day' )
18583 header = this.header;
18586 var cal_heads = function() {
18588 // fixme - handle this.
18590 for (var i =0; i < Date.dayNames.length; i++) {
18591 var d = Date.dayNames[i];
18594 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18595 html : d.substring(0,3)
18599 ret[0].cls += ' fc-first';
18600 ret[6].cls += ' fc-last';
18603 var cal_cell = function(n) {
18606 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18611 cls: 'fc-day-number',
18615 cls: 'fc-day-content',
18619 style: 'position: relative;' // height: 17px;
18631 var cal_rows = function() {
18634 for (var r = 0; r < 6; r++) {
18641 for (var i =0; i < Date.dayNames.length; i++) {
18642 var d = Date.dayNames[i];
18643 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18646 row.cn[0].cls+=' fc-first';
18647 row.cn[0].cn[0].style = 'min-height:90px';
18648 row.cn[6].cls+=' fc-last';
18652 ret[0].cls += ' fc-first';
18653 ret[4].cls += ' fc-prev-last';
18654 ret[5].cls += ' fc-last';
18661 cls: 'fc-border-separate',
18662 style : 'width:100%',
18670 cls : 'fc-first fc-last',
18688 cls : 'fc-content',
18689 style : "position: relative;",
18692 cls : 'fc-view fc-view-month fc-grid',
18693 style : 'position: relative',
18694 unselectable : 'on',
18697 cls : 'fc-event-container',
18698 style : 'position:absolute;z-index:8;top:0;left:0;'
18716 initEvents : function()
18719 throw "can not find store for calendar";
18725 style: "text-align:center",
18729 style: "background-color:white;width:50%;margin:250 auto",
18733 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18744 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18746 var size = this.el.select('.fc-content', true).first().getSize();
18747 this.maskEl.setSize(size.width, size.height);
18748 this.maskEl.enableDisplayMode("block");
18749 if(!this.loadMask){
18750 this.maskEl.hide();
18753 this.store = Roo.factory(this.store, Roo.data);
18754 this.store.on('load', this.onLoad, this);
18755 this.store.on('beforeload', this.onBeforeLoad, this);
18759 this.cells = this.el.select('.fc-day',true);
18760 //Roo.log(this.cells);
18761 this.textNodes = this.el.query('.fc-day-number');
18762 this.cells.addClassOnOver('fc-state-hover');
18764 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18765 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18766 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18767 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18769 this.on('monthchange', this.onMonthChange, this);
18771 this.update(new Date().clearTime());
18774 resize : function() {
18775 var sz = this.el.getSize();
18777 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18778 this.el.select('.fc-day-content div',true).setHeight(34);
18783 showPrevMonth : function(e){
18784 this.update(this.activeDate.add("mo", -1));
18786 showToday : function(e){
18787 this.update(new Date().clearTime());
18790 showNextMonth : function(e){
18791 this.update(this.activeDate.add("mo", 1));
18795 showPrevYear : function(){
18796 this.update(this.activeDate.add("y", -1));
18800 showNextYear : function(){
18801 this.update(this.activeDate.add("y", 1));
18806 update : function(date)
18808 var vd = this.activeDate;
18809 this.activeDate = date;
18810 // if(vd && this.el){
18811 // var t = date.getTime();
18812 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18813 // Roo.log('using add remove');
18815 // this.fireEvent('monthchange', this, date);
18817 // this.cells.removeClass("fc-state-highlight");
18818 // this.cells.each(function(c){
18819 // if(c.dateValue == t){
18820 // c.addClass("fc-state-highlight");
18821 // setTimeout(function(){
18822 // try{c.dom.firstChild.focus();}catch(e){}
18832 var days = date.getDaysInMonth();
18834 var firstOfMonth = date.getFirstDateOfMonth();
18835 var startingPos = firstOfMonth.getDay()-this.startDay;
18837 if(startingPos < this.startDay){
18841 var pm = date.add(Date.MONTH, -1);
18842 var prevStart = pm.getDaysInMonth()-startingPos;
18844 this.cells = this.el.select('.fc-day',true);
18845 this.textNodes = this.el.query('.fc-day-number');
18846 this.cells.addClassOnOver('fc-state-hover');
18848 var cells = this.cells.elements;
18849 var textEls = this.textNodes;
18851 Roo.each(cells, function(cell){
18852 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18855 days += startingPos;
18857 // convert everything to numbers so it's fast
18858 var day = 86400000;
18859 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18862 //Roo.log(prevStart);
18864 var today = new Date().clearTime().getTime();
18865 var sel = date.clearTime().getTime();
18866 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18867 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18868 var ddMatch = this.disabledDatesRE;
18869 var ddText = this.disabledDatesText;
18870 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18871 var ddaysText = this.disabledDaysText;
18872 var format = this.format;
18874 var setCellClass = function(cal, cell){
18878 //Roo.log('set Cell Class');
18880 var t = d.getTime();
18884 cell.dateValue = t;
18886 cell.className += " fc-today";
18887 cell.className += " fc-state-highlight";
18888 cell.title = cal.todayText;
18891 // disable highlight in other month..
18892 //cell.className += " fc-state-highlight";
18897 cell.className = " fc-state-disabled";
18898 cell.title = cal.minText;
18902 cell.className = " fc-state-disabled";
18903 cell.title = cal.maxText;
18907 if(ddays.indexOf(d.getDay()) != -1){
18908 cell.title = ddaysText;
18909 cell.className = " fc-state-disabled";
18912 if(ddMatch && format){
18913 var fvalue = d.dateFormat(format);
18914 if(ddMatch.test(fvalue)){
18915 cell.title = ddText.replace("%0", fvalue);
18916 cell.className = " fc-state-disabled";
18920 if (!cell.initialClassName) {
18921 cell.initialClassName = cell.dom.className;
18924 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18929 for(; i < startingPos; i++) {
18930 textEls[i].innerHTML = (++prevStart);
18931 d.setDate(d.getDate()+1);
18933 cells[i].className = "fc-past fc-other-month";
18934 setCellClass(this, cells[i]);
18939 for(; i < days; i++){
18940 intDay = i - startingPos + 1;
18941 textEls[i].innerHTML = (intDay);
18942 d.setDate(d.getDate()+1);
18944 cells[i].className = ''; // "x-date-active";
18945 setCellClass(this, cells[i]);
18949 for(; i < 42; i++) {
18950 textEls[i].innerHTML = (++extraDays);
18951 d.setDate(d.getDate()+1);
18953 cells[i].className = "fc-future fc-other-month";
18954 setCellClass(this, cells[i]);
18957 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18959 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18961 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18962 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18964 if(totalRows != 6){
18965 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18966 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18969 this.fireEvent('monthchange', this, date);
18973 if(!this.internalRender){
18974 var main = this.el.dom.firstChild;
18975 var w = main.offsetWidth;
18976 this.el.setWidth(w + this.el.getBorderWidth("lr"));
18977 Roo.fly(main).setWidth(w);
18978 this.internalRender = true;
18979 // opera does not respect the auto grow header center column
18980 // then, after it gets a width opera refuses to recalculate
18981 // without a second pass
18982 if(Roo.isOpera && !this.secondPass){
18983 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18984 this.secondPass = true;
18985 this.update.defer(10, this, [date]);
18992 findCell : function(dt) {
18993 dt = dt.clearTime().getTime();
18995 this.cells.each(function(c){
18996 //Roo.log("check " +c.dateValue + '?=' + dt);
18997 if(c.dateValue == dt){
19007 findCells : function(ev) {
19008 var s = ev.start.clone().clearTime().getTime();
19010 var e= ev.end.clone().clearTime().getTime();
19013 this.cells.each(function(c){
19014 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19016 if(c.dateValue > e){
19019 if(c.dateValue < s){
19028 // findBestRow: function(cells)
19032 // for (var i =0 ; i < cells.length;i++) {
19033 // ret = Math.max(cells[i].rows || 0,ret);
19040 addItem : function(ev)
19042 // look for vertical location slot in
19043 var cells = this.findCells(ev);
19045 // ev.row = this.findBestRow(cells);
19047 // work out the location.
19051 for(var i =0; i < cells.length; i++) {
19053 cells[i].row = cells[0].row;
19056 cells[i].row = cells[i].row + 1;
19066 if (crow.start.getY() == cells[i].getY()) {
19068 crow.end = cells[i];
19085 cells[0].events.push(ev);
19087 this.calevents.push(ev);
19090 clearEvents: function() {
19092 if(!this.calevents){
19096 Roo.each(this.cells.elements, function(c){
19102 Roo.each(this.calevents, function(e) {
19103 Roo.each(e.els, function(el) {
19104 el.un('mouseenter' ,this.onEventEnter, this);
19105 el.un('mouseleave' ,this.onEventLeave, this);
19110 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19116 renderEvents: function()
19120 this.cells.each(function(c) {
19129 if(c.row != c.events.length){
19130 r = 4 - (4 - (c.row - c.events.length));
19133 c.events = ev.slice(0, r);
19134 c.more = ev.slice(r);
19136 if(c.more.length && c.more.length == 1){
19137 c.events.push(c.more.pop());
19140 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19144 this.cells.each(function(c) {
19146 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19149 for (var e = 0; e < c.events.length; e++){
19150 var ev = c.events[e];
19151 var rows = ev.rows;
19153 for(var i = 0; i < rows.length; i++) {
19155 // how many rows should it span..
19158 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19159 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19161 unselectable : "on",
19164 cls: 'fc-event-inner',
19168 // cls: 'fc-event-time',
19169 // html : cells.length > 1 ? '' : ev.time
19173 cls: 'fc-event-title',
19174 html : String.format('{0}', ev.title)
19181 cls: 'ui-resizable-handle ui-resizable-e',
19182 html : '  '
19189 cfg.cls += ' fc-event-start';
19191 if ((i+1) == rows.length) {
19192 cfg.cls += ' fc-event-end';
19195 var ctr = _this.el.select('.fc-event-container',true).first();
19196 var cg = ctr.createChild(cfg);
19198 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19199 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19201 var r = (c.more.length) ? 1 : 0;
19202 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19203 cg.setWidth(ebox.right - sbox.x -2);
19205 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19206 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19207 cg.on('click', _this.onEventClick, _this, ev);
19218 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19219 style : 'position: absolute',
19220 unselectable : "on",
19223 cls: 'fc-event-inner',
19227 cls: 'fc-event-title',
19235 cls: 'ui-resizable-handle ui-resizable-e',
19236 html : '  '
19242 var ctr = _this.el.select('.fc-event-container',true).first();
19243 var cg = ctr.createChild(cfg);
19245 var sbox = c.select('.fc-day-content',true).first().getBox();
19246 var ebox = c.select('.fc-day-content',true).first().getBox();
19248 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19249 cg.setWidth(ebox.right - sbox.x -2);
19251 cg.on('click', _this.onMoreEventClick, _this, c.more);
19261 onEventEnter: function (e, el,event,d) {
19262 this.fireEvent('evententer', this, el, event);
19265 onEventLeave: function (e, el,event,d) {
19266 this.fireEvent('eventleave', this, el, event);
19269 onEventClick: function (e, el,event,d) {
19270 this.fireEvent('eventclick', this, el, event);
19273 onMonthChange: function () {
19277 onMoreEventClick: function(e, el, more)
19281 this.calpopover.placement = 'right';
19282 this.calpopover.setTitle('More');
19284 this.calpopover.setContent('');
19286 var ctr = this.calpopover.el.select('.popover-content', true).first();
19288 Roo.each(more, function(m){
19290 cls : 'fc-event-hori fc-event-draggable',
19293 var cg = ctr.createChild(cfg);
19295 cg.on('click', _this.onEventClick, _this, m);
19298 this.calpopover.show(el);
19303 onLoad: function ()
19305 this.calevents = [];
19308 if(this.store.getCount() > 0){
19309 this.store.data.each(function(d){
19312 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19313 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19314 time : d.data.start_time,
19315 title : d.data.title,
19316 description : d.data.description,
19317 venue : d.data.venue
19322 this.renderEvents();
19324 if(this.calevents.length && this.loadMask){
19325 this.maskEl.hide();
19329 onBeforeLoad: function()
19331 this.clearEvents();
19333 this.maskEl.show();
19347 * @class Roo.bootstrap.Popover
19348 * @extends Roo.bootstrap.Component
19349 * Bootstrap Popover class
19350 * @cfg {String} html contents of the popover (or false to use children..)
19351 * @cfg {String} title of popover (or false to hide)
19352 * @cfg {String} placement how it is placed
19353 * @cfg {String} trigger click || hover (or false to trigger manually)
19354 * @cfg {String} over what (parent or false to trigger manually.)
19355 * @cfg {Number} delay - delay before showing
19358 * Create a new Popover
19359 * @param {Object} config The config object
19362 Roo.bootstrap.Popover = function(config){
19363 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19369 * After the popover show
19371 * @param {Roo.bootstrap.Popover} this
19376 * After the popover hide
19378 * @param {Roo.bootstrap.Popover} this
19384 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19386 title: 'Fill in a title',
19389 placement : 'right',
19390 trigger : 'hover', // hover
19396 can_build_overlaid : false,
19398 getChildContainer : function()
19400 return this.el.select('.popover-content',true).first();
19403 getAutoCreate : function(){
19406 cls : 'popover roo-dynamic',
19407 style: 'display:block',
19413 cls : 'popover-inner',
19417 cls: 'popover-title popover-header',
19421 cls : 'popover-content popover-body',
19432 setTitle: function(str)
19435 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19437 setContent: function(str)
19440 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19442 // as it get's added to the bottom of the page.
19443 onRender : function(ct, position)
19445 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19447 var cfg = Roo.apply({}, this.getAutoCreate());
19451 cfg.cls += ' ' + this.cls;
19454 cfg.style = this.style;
19456 //Roo.log("adding to ");
19457 this.el = Roo.get(document.body).createChild(cfg, position);
19458 // Roo.log(this.el);
19463 initEvents : function()
19465 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19466 this.el.enableDisplayMode('block');
19468 if (this.over === false) {
19471 if (this.triggers === false) {
19474 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19475 var triggers = this.trigger ? this.trigger.split(' ') : [];
19476 Roo.each(triggers, function(trigger) {
19478 if (trigger == 'click') {
19479 on_el.on('click', this.toggle, this);
19480 } else if (trigger != 'manual') {
19481 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19482 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19484 on_el.on(eventIn ,this.enter, this);
19485 on_el.on(eventOut, this.leave, this);
19496 toggle : function () {
19497 this.hoverState == 'in' ? this.leave() : this.enter();
19500 enter : function () {
19502 clearTimeout(this.timeout);
19504 this.hoverState = 'in';
19506 if (!this.delay || !this.delay.show) {
19511 this.timeout = setTimeout(function () {
19512 if (_t.hoverState == 'in') {
19515 }, this.delay.show)
19518 leave : function() {
19519 clearTimeout(this.timeout);
19521 this.hoverState = 'out';
19523 if (!this.delay || !this.delay.hide) {
19528 this.timeout = setTimeout(function () {
19529 if (_t.hoverState == 'out') {
19532 }, this.delay.hide)
19535 show : function (on_el)
19538 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19542 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19543 if (this.html !== false) {
19544 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19546 this.el.removeClass([
19547 'fade','top','bottom', 'left', 'right','in',
19548 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19550 if (!this.title.length) {
19551 this.el.select('.popover-title',true).hide();
19554 var placement = typeof this.placement == 'function' ?
19555 this.placement.call(this, this.el, on_el) :
19558 var autoToken = /\s?auto?\s?/i;
19559 var autoPlace = autoToken.test(placement);
19561 placement = placement.replace(autoToken, '') || 'top';
19565 //this.el.setXY([0,0]);
19567 this.el.dom.style.display='block';
19568 this.el.addClass(placement);
19570 //this.el.appendTo(on_el);
19572 var p = this.getPosition();
19573 var box = this.el.getBox();
19578 var align = Roo.bootstrap.Popover.alignment[placement];
19581 this.el.alignTo(on_el, align[0],align[1]);
19582 //var arrow = this.el.select('.arrow',true).first();
19583 //arrow.set(align[2],
19585 this.el.addClass('in');
19588 if (this.el.hasClass('fade')) {
19592 this.hoverState = 'in';
19594 this.fireEvent('show', this);
19599 this.el.setXY([0,0]);
19600 this.el.removeClass('in');
19602 this.hoverState = null;
19604 this.fireEvent('hide', this);
19609 Roo.bootstrap.Popover.alignment = {
19610 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19611 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19612 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19613 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19624 * @class Roo.bootstrap.Progress
19625 * @extends Roo.bootstrap.Component
19626 * Bootstrap Progress class
19627 * @cfg {Boolean} striped striped of the progress bar
19628 * @cfg {Boolean} active animated of the progress bar
19632 * Create a new Progress
19633 * @param {Object} config The config object
19636 Roo.bootstrap.Progress = function(config){
19637 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19640 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19645 getAutoCreate : function(){
19653 cfg.cls += ' progress-striped';
19657 cfg.cls += ' active';
19676 * @class Roo.bootstrap.ProgressBar
19677 * @extends Roo.bootstrap.Component
19678 * Bootstrap ProgressBar class
19679 * @cfg {Number} aria_valuenow aria-value now
19680 * @cfg {Number} aria_valuemin aria-value min
19681 * @cfg {Number} aria_valuemax aria-value max
19682 * @cfg {String} label label for the progress bar
19683 * @cfg {String} panel (success | info | warning | danger )
19684 * @cfg {String} role role of the progress bar
19685 * @cfg {String} sr_only text
19689 * Create a new ProgressBar
19690 * @param {Object} config The config object
19693 Roo.bootstrap.ProgressBar = function(config){
19694 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19697 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19701 aria_valuemax : 100,
19707 getAutoCreate : function()
19712 cls: 'progress-bar',
19713 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19725 cfg.role = this.role;
19728 if(this.aria_valuenow){
19729 cfg['aria-valuenow'] = this.aria_valuenow;
19732 if(this.aria_valuemin){
19733 cfg['aria-valuemin'] = this.aria_valuemin;
19736 if(this.aria_valuemax){
19737 cfg['aria-valuemax'] = this.aria_valuemax;
19740 if(this.label && !this.sr_only){
19741 cfg.html = this.label;
19745 cfg.cls += ' progress-bar-' + this.panel;
19751 update : function(aria_valuenow)
19753 this.aria_valuenow = aria_valuenow;
19755 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19770 * @class Roo.bootstrap.TabGroup
19771 * @extends Roo.bootstrap.Column
19772 * Bootstrap Column class
19773 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19774 * @cfg {Boolean} carousel true to make the group behave like a carousel
19775 * @cfg {Boolean} bullets show bullets for the panels
19776 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19777 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19778 * @cfg {Boolean} showarrow (true|false) show arrow default true
19781 * Create a new TabGroup
19782 * @param {Object} config The config object
19785 Roo.bootstrap.TabGroup = function(config){
19786 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19788 this.navId = Roo.id();
19791 Roo.bootstrap.TabGroup.register(this);
19795 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19798 transition : false,
19803 slideOnTouch : false,
19806 getAutoCreate : function()
19808 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19810 cfg.cls += ' tab-content';
19812 if (this.carousel) {
19813 cfg.cls += ' carousel slide';
19816 cls : 'carousel-inner',
19820 if(this.bullets && !Roo.isTouch){
19823 cls : 'carousel-bullets',
19827 if(this.bullets_cls){
19828 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19835 cfg.cn[0].cn.push(bullets);
19838 if(this.showarrow){
19839 cfg.cn[0].cn.push({
19841 class : 'carousel-arrow',
19845 class : 'carousel-prev',
19849 class : 'fa fa-chevron-left'
19855 class : 'carousel-next',
19859 class : 'fa fa-chevron-right'
19872 initEvents: function()
19874 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19875 // this.el.on("touchstart", this.onTouchStart, this);
19878 if(this.autoslide){
19881 this.slideFn = window.setInterval(function() {
19882 _this.showPanelNext();
19886 if(this.showarrow){
19887 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19888 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19894 // onTouchStart : function(e, el, o)
19896 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19900 // this.showPanelNext();
19904 getChildContainer : function()
19906 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19910 * register a Navigation item
19911 * @param {Roo.bootstrap.NavItem} the navitem to add
19913 register : function(item)
19915 this.tabs.push( item);
19916 item.navId = this.navId; // not really needed..
19921 getActivePanel : function()
19924 Roo.each(this.tabs, function(t) {
19934 getPanelByName : function(n)
19937 Roo.each(this.tabs, function(t) {
19938 if (t.tabId == n) {
19946 indexOfPanel : function(p)
19949 Roo.each(this.tabs, function(t,i) {
19950 if (t.tabId == p.tabId) {
19959 * show a specific panel
19960 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19961 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19963 showPanel : function (pan)
19965 if(this.transition || typeof(pan) == 'undefined'){
19966 Roo.log("waiting for the transitionend");
19970 if (typeof(pan) == 'number') {
19971 pan = this.tabs[pan];
19974 if (typeof(pan) == 'string') {
19975 pan = this.getPanelByName(pan);
19978 var cur = this.getActivePanel();
19981 Roo.log('pan or acitve pan is undefined');
19985 if (pan.tabId == this.getActivePanel().tabId) {
19989 if (false === cur.fireEvent('beforedeactivate')) {
19993 if(this.bullets > 0 && !Roo.isTouch){
19994 this.setActiveBullet(this.indexOfPanel(pan));
19997 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19999 //class="carousel-item carousel-item-next carousel-item-left"
20001 this.transition = true;
20002 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20003 var lr = dir == 'next' ? 'left' : 'right';
20004 pan.el.addClass(dir); // or prev
20005 pan.el.addClass('carousel-item-' + dir); // or prev
20006 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20007 cur.el.addClass(lr); // or right
20008 pan.el.addClass(lr);
20009 cur.el.addClass('carousel-item-' +lr); // or right
20010 pan.el.addClass('carousel-item-' +lr);
20014 cur.el.on('transitionend', function() {
20015 Roo.log("trans end?");
20017 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20018 pan.setActive(true);
20020 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20021 cur.setActive(false);
20023 _this.transition = false;
20025 }, this, { single: true } );
20030 cur.setActive(false);
20031 pan.setActive(true);
20036 showPanelNext : function()
20038 var i = this.indexOfPanel(this.getActivePanel());
20040 if (i >= this.tabs.length - 1 && !this.autoslide) {
20044 if (i >= this.tabs.length - 1 && this.autoslide) {
20048 this.showPanel(this.tabs[i+1]);
20051 showPanelPrev : function()
20053 var i = this.indexOfPanel(this.getActivePanel());
20055 if (i < 1 && !this.autoslide) {
20059 if (i < 1 && this.autoslide) {
20060 i = this.tabs.length;
20063 this.showPanel(this.tabs[i-1]);
20067 addBullet: function()
20069 if(!this.bullets || Roo.isTouch){
20072 var ctr = this.el.select('.carousel-bullets',true).first();
20073 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20074 var bullet = ctr.createChild({
20075 cls : 'bullet bullet-' + i
20076 },ctr.dom.lastChild);
20081 bullet.on('click', (function(e, el, o, ii, t){
20083 e.preventDefault();
20085 this.showPanel(ii);
20087 if(this.autoslide && this.slideFn){
20088 clearInterval(this.slideFn);
20089 this.slideFn = window.setInterval(function() {
20090 _this.showPanelNext();
20094 }).createDelegate(this, [i, bullet], true));
20099 setActiveBullet : function(i)
20105 Roo.each(this.el.select('.bullet', true).elements, function(el){
20106 el.removeClass('selected');
20109 var bullet = this.el.select('.bullet-' + i, true).first();
20115 bullet.addClass('selected');
20126 Roo.apply(Roo.bootstrap.TabGroup, {
20130 * register a Navigation Group
20131 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20133 register : function(navgrp)
20135 this.groups[navgrp.navId] = navgrp;
20139 * fetch a Navigation Group based on the navigation ID
20140 * if one does not exist , it will get created.
20141 * @param {string} the navgroup to add
20142 * @returns {Roo.bootstrap.NavGroup} the navgroup
20144 get: function(navId) {
20145 if (typeof(this.groups[navId]) == 'undefined') {
20146 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20148 return this.groups[navId] ;
20163 * @class Roo.bootstrap.TabPanel
20164 * @extends Roo.bootstrap.Component
20165 * Bootstrap TabPanel class
20166 * @cfg {Boolean} active panel active
20167 * @cfg {String} html panel content
20168 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20169 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20170 * @cfg {String} href click to link..
20174 * Create a new TabPanel
20175 * @param {Object} config The config object
20178 Roo.bootstrap.TabPanel = function(config){
20179 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20183 * Fires when the active status changes
20184 * @param {Roo.bootstrap.TabPanel} this
20185 * @param {Boolean} state the new state
20190 * @event beforedeactivate
20191 * Fires before a tab is de-activated - can be used to do validation on a form.
20192 * @param {Roo.bootstrap.TabPanel} this
20193 * @return {Boolean} false if there is an error
20196 'beforedeactivate': true
20199 this.tabId = this.tabId || Roo.id();
20203 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20211 getAutoCreate : function(){
20216 // item is needed for carousel - not sure if it has any effect otherwise
20217 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20218 html: this.html || ''
20222 cfg.cls += ' active';
20226 cfg.tabId = this.tabId;
20234 initEvents: function()
20236 var p = this.parent();
20238 this.navId = this.navId || p.navId;
20240 if (typeof(this.navId) != 'undefined') {
20241 // not really needed.. but just in case.. parent should be a NavGroup.
20242 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20246 var i = tg.tabs.length - 1;
20248 if(this.active && tg.bullets > 0 && i < tg.bullets){
20249 tg.setActiveBullet(i);
20253 this.el.on('click', this.onClick, this);
20256 this.el.on("touchstart", this.onTouchStart, this);
20257 this.el.on("touchmove", this.onTouchMove, this);
20258 this.el.on("touchend", this.onTouchEnd, this);
20263 onRender : function(ct, position)
20265 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20268 setActive : function(state)
20270 Roo.log("panel - set active " + this.tabId + "=" + state);
20272 this.active = state;
20274 this.el.removeClass('active');
20276 } else if (!this.el.hasClass('active')) {
20277 this.el.addClass('active');
20280 this.fireEvent('changed', this, state);
20283 onClick : function(e)
20285 e.preventDefault();
20287 if(!this.href.length){
20291 window.location.href = this.href;
20300 onTouchStart : function(e)
20302 this.swiping = false;
20304 this.startX = e.browserEvent.touches[0].clientX;
20305 this.startY = e.browserEvent.touches[0].clientY;
20308 onTouchMove : function(e)
20310 this.swiping = true;
20312 this.endX = e.browserEvent.touches[0].clientX;
20313 this.endY = e.browserEvent.touches[0].clientY;
20316 onTouchEnd : function(e)
20323 var tabGroup = this.parent();
20325 if(this.endX > this.startX){ // swiping right
20326 tabGroup.showPanelPrev();
20330 if(this.startX > this.endX){ // swiping left
20331 tabGroup.showPanelNext();
20350 * @class Roo.bootstrap.DateField
20351 * @extends Roo.bootstrap.Input
20352 * Bootstrap DateField class
20353 * @cfg {Number} weekStart default 0
20354 * @cfg {String} viewMode default empty, (months|years)
20355 * @cfg {String} minViewMode default empty, (months|years)
20356 * @cfg {Number} startDate default -Infinity
20357 * @cfg {Number} endDate default Infinity
20358 * @cfg {Boolean} todayHighlight default false
20359 * @cfg {Boolean} todayBtn default false
20360 * @cfg {Boolean} calendarWeeks default false
20361 * @cfg {Object} daysOfWeekDisabled default empty
20362 * @cfg {Boolean} singleMode default false (true | false)
20364 * @cfg {Boolean} keyboardNavigation default true
20365 * @cfg {String} language default en
20368 * Create a new DateField
20369 * @param {Object} config The config object
20372 Roo.bootstrap.DateField = function(config){
20373 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20377 * Fires when this field show.
20378 * @param {Roo.bootstrap.DateField} this
20379 * @param {Mixed} date The date value
20384 * Fires when this field hide.
20385 * @param {Roo.bootstrap.DateField} this
20386 * @param {Mixed} date The date value
20391 * Fires when select a date.
20392 * @param {Roo.bootstrap.DateField} this
20393 * @param {Mixed} date The date value
20397 * @event beforeselect
20398 * Fires when before select a date.
20399 * @param {Roo.bootstrap.DateField} this
20400 * @param {Mixed} date The date value
20402 beforeselect : true
20406 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20409 * @cfg {String} format
20410 * The default date format string which can be overriden for localization support. The format must be
20411 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20415 * @cfg {String} altFormats
20416 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20417 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20419 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20427 todayHighlight : false,
20433 keyboardNavigation: true,
20435 calendarWeeks: false,
20437 startDate: -Infinity,
20441 daysOfWeekDisabled: [],
20445 singleMode : false,
20447 UTCDate: function()
20449 return new Date(Date.UTC.apply(Date, arguments));
20452 UTCToday: function()
20454 var today = new Date();
20455 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20458 getDate: function() {
20459 var d = this.getUTCDate();
20460 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20463 getUTCDate: function() {
20467 setDate: function(d) {
20468 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20471 setUTCDate: function(d) {
20473 this.setValue(this.formatDate(this.date));
20476 onRender: function(ct, position)
20479 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20481 this.language = this.language || 'en';
20482 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20483 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20485 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20486 this.format = this.format || 'm/d/y';
20487 this.isInline = false;
20488 this.isInput = true;
20489 this.component = this.el.select('.add-on', true).first() || false;
20490 this.component = (this.component && this.component.length === 0) ? false : this.component;
20491 this.hasInput = this.component && this.inputEl().length;
20493 if (typeof(this.minViewMode === 'string')) {
20494 switch (this.minViewMode) {
20496 this.minViewMode = 1;
20499 this.minViewMode = 2;
20502 this.minViewMode = 0;
20507 if (typeof(this.viewMode === 'string')) {
20508 switch (this.viewMode) {
20521 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20523 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20525 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20527 this.picker().on('mousedown', this.onMousedown, this);
20528 this.picker().on('click', this.onClick, this);
20530 this.picker().addClass('datepicker-dropdown');
20532 this.startViewMode = this.viewMode;
20534 if(this.singleMode){
20535 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20536 v.setVisibilityMode(Roo.Element.DISPLAY);
20540 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20541 v.setStyle('width', '189px');
20545 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20546 if(!this.calendarWeeks){
20551 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20552 v.attr('colspan', function(i, val){
20553 return parseInt(val) + 1;
20558 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20560 this.setStartDate(this.startDate);
20561 this.setEndDate(this.endDate);
20563 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20570 if(this.isInline) {
20575 picker : function()
20577 return this.pickerEl;
20578 // return this.el.select('.datepicker', true).first();
20581 fillDow: function()
20583 var dowCnt = this.weekStart;
20592 if(this.calendarWeeks){
20600 while (dowCnt < this.weekStart + 7) {
20604 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20608 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20611 fillMonths: function()
20614 var months = this.picker().select('>.datepicker-months td', true).first();
20616 months.dom.innerHTML = '';
20622 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20625 months.createChild(month);
20632 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;
20634 if (this.date < this.startDate) {
20635 this.viewDate = new Date(this.startDate);
20636 } else if (this.date > this.endDate) {
20637 this.viewDate = new Date(this.endDate);
20639 this.viewDate = new Date(this.date);
20647 var d = new Date(this.viewDate),
20648 year = d.getUTCFullYear(),
20649 month = d.getUTCMonth(),
20650 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20651 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20652 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20653 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20654 currentDate = this.date && this.date.valueOf(),
20655 today = this.UTCToday();
20657 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20659 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20661 // this.picker.select('>tfoot th.today').
20662 // .text(dates[this.language].today)
20663 // .toggle(this.todayBtn !== false);
20665 this.updateNavArrows();
20668 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20670 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20672 prevMonth.setUTCDate(day);
20674 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20676 var nextMonth = new Date(prevMonth);
20678 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20680 nextMonth = nextMonth.valueOf();
20682 var fillMonths = false;
20684 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20686 while(prevMonth.valueOf() <= nextMonth) {
20689 if (prevMonth.getUTCDay() === this.weekStart) {
20691 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20699 if(this.calendarWeeks){
20700 // ISO 8601: First week contains first thursday.
20701 // ISO also states week starts on Monday, but we can be more abstract here.
20703 // Start of current week: based on weekstart/current date
20704 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20705 // Thursday of this week
20706 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20707 // First Thursday of year, year from thursday
20708 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20709 // Calendar week: ms between thursdays, div ms per day, div 7 days
20710 calWeek = (th - yth) / 864e5 / 7 + 1;
20712 fillMonths.cn.push({
20720 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20722 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20725 if (this.todayHighlight &&
20726 prevMonth.getUTCFullYear() == today.getFullYear() &&
20727 prevMonth.getUTCMonth() == today.getMonth() &&
20728 prevMonth.getUTCDate() == today.getDate()) {
20729 clsName += ' today';
20732 if (currentDate && prevMonth.valueOf() === currentDate) {
20733 clsName += ' active';
20736 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20737 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20738 clsName += ' disabled';
20741 fillMonths.cn.push({
20743 cls: 'day ' + clsName,
20744 html: prevMonth.getDate()
20747 prevMonth.setDate(prevMonth.getDate()+1);
20750 var currentYear = this.date && this.date.getUTCFullYear();
20751 var currentMonth = this.date && this.date.getUTCMonth();
20753 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20755 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20756 v.removeClass('active');
20758 if(currentYear === year && k === currentMonth){
20759 v.addClass('active');
20762 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20763 v.addClass('disabled');
20769 year = parseInt(year/10, 10) * 10;
20771 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20773 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20776 for (var i = -1; i < 11; i++) {
20777 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20779 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20787 showMode: function(dir)
20790 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20793 Roo.each(this.picker().select('>div',true).elements, function(v){
20794 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20797 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20802 if(this.isInline) {
20806 this.picker().removeClass(['bottom', 'top']);
20808 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20810 * place to the top of element!
20814 this.picker().addClass('top');
20815 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20820 this.picker().addClass('bottom');
20822 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20825 parseDate : function(value)
20827 if(!value || value instanceof Date){
20830 var v = Date.parseDate(value, this.format);
20831 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20832 v = Date.parseDate(value, 'Y-m-d');
20834 if(!v && this.altFormats){
20835 if(!this.altFormatsArray){
20836 this.altFormatsArray = this.altFormats.split("|");
20838 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20839 v = Date.parseDate(value, this.altFormatsArray[i]);
20845 formatDate : function(date, fmt)
20847 return (!date || !(date instanceof Date)) ?
20848 date : date.dateFormat(fmt || this.format);
20851 onFocus : function()
20853 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20857 onBlur : function()
20859 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20861 var d = this.inputEl().getValue();
20868 showPopup : function()
20870 this.picker().show();
20874 this.fireEvent('showpopup', this, this.date);
20877 hidePopup : function()
20879 if(this.isInline) {
20882 this.picker().hide();
20883 this.viewMode = this.startViewMode;
20886 this.fireEvent('hidepopup', this, this.date);
20890 onMousedown: function(e)
20892 e.stopPropagation();
20893 e.preventDefault();
20898 Roo.bootstrap.DateField.superclass.keyup.call(this);
20902 setValue: function(v)
20904 if(this.fireEvent('beforeselect', this, v) !== false){
20905 var d = new Date(this.parseDate(v) ).clearTime();
20907 if(isNaN(d.getTime())){
20908 this.date = this.viewDate = '';
20909 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20913 v = this.formatDate(d);
20915 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20917 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20921 this.fireEvent('select', this, this.date);
20925 getValue: function()
20927 return this.formatDate(this.date);
20930 fireKey: function(e)
20932 if (!this.picker().isVisible()){
20933 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20939 var dateChanged = false,
20941 newDate, newViewDate;
20946 e.preventDefault();
20950 if (!this.keyboardNavigation) {
20953 dir = e.keyCode == 37 ? -1 : 1;
20956 newDate = this.moveYear(this.date, dir);
20957 newViewDate = this.moveYear(this.viewDate, dir);
20958 } else if (e.shiftKey){
20959 newDate = this.moveMonth(this.date, dir);
20960 newViewDate = this.moveMonth(this.viewDate, dir);
20962 newDate = new Date(this.date);
20963 newDate.setUTCDate(this.date.getUTCDate() + dir);
20964 newViewDate = new Date(this.viewDate);
20965 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20967 if (this.dateWithinRange(newDate)){
20968 this.date = newDate;
20969 this.viewDate = newViewDate;
20970 this.setValue(this.formatDate(this.date));
20972 e.preventDefault();
20973 dateChanged = true;
20978 if (!this.keyboardNavigation) {
20981 dir = e.keyCode == 38 ? -1 : 1;
20983 newDate = this.moveYear(this.date, dir);
20984 newViewDate = this.moveYear(this.viewDate, dir);
20985 } else if (e.shiftKey){
20986 newDate = this.moveMonth(this.date, dir);
20987 newViewDate = this.moveMonth(this.viewDate, dir);
20989 newDate = new Date(this.date);
20990 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20991 newViewDate = new Date(this.viewDate);
20992 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20994 if (this.dateWithinRange(newDate)){
20995 this.date = newDate;
20996 this.viewDate = newViewDate;
20997 this.setValue(this.formatDate(this.date));
20999 e.preventDefault();
21000 dateChanged = true;
21004 this.setValue(this.formatDate(this.date));
21006 e.preventDefault();
21009 this.setValue(this.formatDate(this.date));
21023 onClick: function(e)
21025 e.stopPropagation();
21026 e.preventDefault();
21028 var target = e.getTarget();
21030 if(target.nodeName.toLowerCase() === 'i'){
21031 target = Roo.get(target).dom.parentNode;
21034 var nodeName = target.nodeName;
21035 var className = target.className;
21036 var html = target.innerHTML;
21037 //Roo.log(nodeName);
21039 switch(nodeName.toLowerCase()) {
21041 switch(className) {
21047 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21048 switch(this.viewMode){
21050 this.viewDate = this.moveMonth(this.viewDate, dir);
21054 this.viewDate = this.moveYear(this.viewDate, dir);
21060 var date = new Date();
21061 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21063 this.setValue(this.formatDate(this.date));
21070 if (className.indexOf('disabled') < 0) {
21071 this.viewDate.setUTCDate(1);
21072 if (className.indexOf('month') > -1) {
21073 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21075 var year = parseInt(html, 10) || 0;
21076 this.viewDate.setUTCFullYear(year);
21080 if(this.singleMode){
21081 this.setValue(this.formatDate(this.viewDate));
21092 //Roo.log(className);
21093 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21094 var day = parseInt(html, 10) || 1;
21095 var year = this.viewDate.getUTCFullYear(),
21096 month = this.viewDate.getUTCMonth();
21098 if (className.indexOf('old') > -1) {
21105 } else if (className.indexOf('new') > -1) {
21113 //Roo.log([year,month,day]);
21114 this.date = this.UTCDate(year, month, day,0,0,0,0);
21115 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21117 //Roo.log(this.formatDate(this.date));
21118 this.setValue(this.formatDate(this.date));
21125 setStartDate: function(startDate)
21127 this.startDate = startDate || -Infinity;
21128 if (this.startDate !== -Infinity) {
21129 this.startDate = this.parseDate(this.startDate);
21132 this.updateNavArrows();
21135 setEndDate: function(endDate)
21137 this.endDate = endDate || Infinity;
21138 if (this.endDate !== Infinity) {
21139 this.endDate = this.parseDate(this.endDate);
21142 this.updateNavArrows();
21145 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21147 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21148 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21149 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21151 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21152 return parseInt(d, 10);
21155 this.updateNavArrows();
21158 updateNavArrows: function()
21160 if(this.singleMode){
21164 var d = new Date(this.viewDate),
21165 year = d.getUTCFullYear(),
21166 month = d.getUTCMonth();
21168 Roo.each(this.picker().select('.prev', true).elements, function(v){
21170 switch (this.viewMode) {
21173 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21179 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21186 Roo.each(this.picker().select('.next', true).elements, function(v){
21188 switch (this.viewMode) {
21191 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21197 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21205 moveMonth: function(date, dir)
21210 var new_date = new Date(date.valueOf()),
21211 day = new_date.getUTCDate(),
21212 month = new_date.getUTCMonth(),
21213 mag = Math.abs(dir),
21215 dir = dir > 0 ? 1 : -1;
21218 // If going back one month, make sure month is not current month
21219 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21221 return new_date.getUTCMonth() == month;
21223 // If going forward one month, make sure month is as expected
21224 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21226 return new_date.getUTCMonth() != new_month;
21228 new_month = month + dir;
21229 new_date.setUTCMonth(new_month);
21230 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21231 if (new_month < 0 || new_month > 11) {
21232 new_month = (new_month + 12) % 12;
21235 // For magnitudes >1, move one month at a time...
21236 for (var i=0; i<mag; i++) {
21237 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21238 new_date = this.moveMonth(new_date, dir);
21240 // ...then reset the day, keeping it in the new month
21241 new_month = new_date.getUTCMonth();
21242 new_date.setUTCDate(day);
21244 return new_month != new_date.getUTCMonth();
21247 // Common date-resetting loop -- if date is beyond end of month, make it
21250 new_date.setUTCDate(--day);
21251 new_date.setUTCMonth(new_month);
21256 moveYear: function(date, dir)
21258 return this.moveMonth(date, dir*12);
21261 dateWithinRange: function(date)
21263 return date >= this.startDate && date <= this.endDate;
21269 this.picker().remove();
21272 validateValue : function(value)
21274 if(this.getVisibilityEl().hasClass('hidden')){
21278 if(value.length < 1) {
21279 if(this.allowBlank){
21285 if(value.length < this.minLength){
21288 if(value.length > this.maxLength){
21292 var vt = Roo.form.VTypes;
21293 if(!vt[this.vtype](value, this)){
21297 if(typeof this.validator == "function"){
21298 var msg = this.validator(value);
21304 if(this.regex && !this.regex.test(value)){
21308 if(typeof(this.parseDate(value)) == 'undefined'){
21312 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21316 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21326 this.date = this.viewDate = '';
21328 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21333 Roo.apply(Roo.bootstrap.DateField, {
21344 html: '<i class="fa fa-arrow-left"/>'
21354 html: '<i class="fa fa-arrow-right"/>'
21396 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21397 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21398 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21399 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21400 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21413 navFnc: 'FullYear',
21418 navFnc: 'FullYear',
21423 Roo.apply(Roo.bootstrap.DateField, {
21427 cls: 'datepicker dropdown-menu roo-dynamic',
21431 cls: 'datepicker-days',
21435 cls: 'table-condensed',
21437 Roo.bootstrap.DateField.head,
21441 Roo.bootstrap.DateField.footer
21448 cls: 'datepicker-months',
21452 cls: 'table-condensed',
21454 Roo.bootstrap.DateField.head,
21455 Roo.bootstrap.DateField.content,
21456 Roo.bootstrap.DateField.footer
21463 cls: 'datepicker-years',
21467 cls: 'table-condensed',
21469 Roo.bootstrap.DateField.head,
21470 Roo.bootstrap.DateField.content,
21471 Roo.bootstrap.DateField.footer
21490 * @class Roo.bootstrap.TimeField
21491 * @extends Roo.bootstrap.Input
21492 * Bootstrap DateField class
21496 * Create a new TimeField
21497 * @param {Object} config The config object
21500 Roo.bootstrap.TimeField = function(config){
21501 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21505 * Fires when this field show.
21506 * @param {Roo.bootstrap.DateField} thisthis
21507 * @param {Mixed} date The date value
21512 * Fires when this field hide.
21513 * @param {Roo.bootstrap.DateField} this
21514 * @param {Mixed} date The date value
21519 * Fires when select a date.
21520 * @param {Roo.bootstrap.DateField} this
21521 * @param {Mixed} date The date value
21527 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21530 * @cfg {String} format
21531 * The default time format string which can be overriden for localization support. The format must be
21532 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21536 onRender: function(ct, position)
21539 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21541 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21543 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21545 this.pop = this.picker().select('>.datepicker-time',true).first();
21546 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21548 this.picker().on('mousedown', this.onMousedown, this);
21549 this.picker().on('click', this.onClick, this);
21551 this.picker().addClass('datepicker-dropdown');
21556 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21557 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21558 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21559 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21560 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21561 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21565 fireKey: function(e){
21566 if (!this.picker().isVisible()){
21567 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21573 e.preventDefault();
21581 this.onTogglePeriod();
21584 this.onIncrementMinutes();
21587 this.onDecrementMinutes();
21596 onClick: function(e) {
21597 e.stopPropagation();
21598 e.preventDefault();
21601 picker : function()
21603 return this.el.select('.datepicker', true).first();
21606 fillTime: function()
21608 var time = this.pop.select('tbody', true).first();
21610 time.dom.innerHTML = '';
21625 cls: 'hours-up glyphicon glyphicon-chevron-up'
21645 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21666 cls: 'timepicker-hour',
21681 cls: 'timepicker-minute',
21696 cls: 'btn btn-primary period',
21718 cls: 'hours-down glyphicon glyphicon-chevron-down'
21738 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21756 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21763 var hours = this.time.getHours();
21764 var minutes = this.time.getMinutes();
21777 hours = hours - 12;
21781 hours = '0' + hours;
21785 minutes = '0' + minutes;
21788 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21789 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21790 this.pop.select('button', true).first().dom.innerHTML = period;
21796 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21798 var cls = ['bottom'];
21800 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21807 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21812 this.picker().addClass(cls.join('-'));
21816 Roo.each(cls, function(c){
21818 _this.picker().setTop(_this.inputEl().getHeight());
21822 _this.picker().setTop(0 - _this.picker().getHeight());
21827 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21831 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21838 onFocus : function()
21840 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21844 onBlur : function()
21846 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21852 this.picker().show();
21857 this.fireEvent('show', this, this.date);
21862 this.picker().hide();
21865 this.fireEvent('hide', this, this.date);
21868 setTime : function()
21871 this.setValue(this.time.format(this.format));
21873 this.fireEvent('select', this, this.date);
21878 onMousedown: function(e){
21879 e.stopPropagation();
21880 e.preventDefault();
21883 onIncrementHours: function()
21885 Roo.log('onIncrementHours');
21886 this.time = this.time.add(Date.HOUR, 1);
21891 onDecrementHours: function()
21893 Roo.log('onDecrementHours');
21894 this.time = this.time.add(Date.HOUR, -1);
21898 onIncrementMinutes: function()
21900 Roo.log('onIncrementMinutes');
21901 this.time = this.time.add(Date.MINUTE, 1);
21905 onDecrementMinutes: function()
21907 Roo.log('onDecrementMinutes');
21908 this.time = this.time.add(Date.MINUTE, -1);
21912 onTogglePeriod: function()
21914 Roo.log('onTogglePeriod');
21915 this.time = this.time.add(Date.HOUR, 12);
21922 Roo.apply(Roo.bootstrap.TimeField, {
21952 cls: 'btn btn-info ok',
21964 Roo.apply(Roo.bootstrap.TimeField, {
21968 cls: 'datepicker dropdown-menu',
21972 cls: 'datepicker-time',
21976 cls: 'table-condensed',
21978 Roo.bootstrap.TimeField.content,
21979 Roo.bootstrap.TimeField.footer
21998 * @class Roo.bootstrap.MonthField
21999 * @extends Roo.bootstrap.Input
22000 * Bootstrap MonthField class
22002 * @cfg {String} language default en
22005 * Create a new MonthField
22006 * @param {Object} config The config object
22009 Roo.bootstrap.MonthField = function(config){
22010 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22015 * Fires when this field show.
22016 * @param {Roo.bootstrap.MonthField} this
22017 * @param {Mixed} date The date value
22022 * Fires when this field hide.
22023 * @param {Roo.bootstrap.MonthField} this
22024 * @param {Mixed} date The date value
22029 * Fires when select a date.
22030 * @param {Roo.bootstrap.MonthField} this
22031 * @param {String} oldvalue The old value
22032 * @param {String} newvalue The new value
22038 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22040 onRender: function(ct, position)
22043 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22045 this.language = this.language || 'en';
22046 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22047 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22049 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22050 this.isInline = false;
22051 this.isInput = true;
22052 this.component = this.el.select('.add-on', true).first() || false;
22053 this.component = (this.component && this.component.length === 0) ? false : this.component;
22054 this.hasInput = this.component && this.inputEL().length;
22056 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22058 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22060 this.picker().on('mousedown', this.onMousedown, this);
22061 this.picker().on('click', this.onClick, this);
22063 this.picker().addClass('datepicker-dropdown');
22065 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22066 v.setStyle('width', '189px');
22073 if(this.isInline) {
22079 setValue: function(v, suppressEvent)
22081 var o = this.getValue();
22083 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22087 if(suppressEvent !== true){
22088 this.fireEvent('select', this, o, v);
22093 getValue: function()
22098 onClick: function(e)
22100 e.stopPropagation();
22101 e.preventDefault();
22103 var target = e.getTarget();
22105 if(target.nodeName.toLowerCase() === 'i'){
22106 target = Roo.get(target).dom.parentNode;
22109 var nodeName = target.nodeName;
22110 var className = target.className;
22111 var html = target.innerHTML;
22113 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22117 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22119 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22125 picker : function()
22127 return this.pickerEl;
22130 fillMonths: function()
22133 var months = this.picker().select('>.datepicker-months td', true).first();
22135 months.dom.innerHTML = '';
22141 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22144 months.createChild(month);
22153 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22154 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22157 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22158 e.removeClass('active');
22160 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22161 e.addClass('active');
22168 if(this.isInline) {
22172 this.picker().removeClass(['bottom', 'top']);
22174 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22176 * place to the top of element!
22180 this.picker().addClass('top');
22181 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22186 this.picker().addClass('bottom');
22188 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22191 onFocus : function()
22193 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22197 onBlur : function()
22199 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22201 var d = this.inputEl().getValue();
22210 this.picker().show();
22211 this.picker().select('>.datepicker-months', true).first().show();
22215 this.fireEvent('show', this, this.date);
22220 if(this.isInline) {
22223 this.picker().hide();
22224 this.fireEvent('hide', this, this.date);
22228 onMousedown: function(e)
22230 e.stopPropagation();
22231 e.preventDefault();
22236 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22240 fireKey: function(e)
22242 if (!this.picker().isVisible()){
22243 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22254 e.preventDefault();
22258 dir = e.keyCode == 37 ? -1 : 1;
22260 this.vIndex = this.vIndex + dir;
22262 if(this.vIndex < 0){
22266 if(this.vIndex > 11){
22270 if(isNaN(this.vIndex)){
22274 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22280 dir = e.keyCode == 38 ? -1 : 1;
22282 this.vIndex = this.vIndex + dir * 4;
22284 if(this.vIndex < 0){
22288 if(this.vIndex > 11){
22292 if(isNaN(this.vIndex)){
22296 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22301 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22302 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22306 e.preventDefault();
22309 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22310 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22326 this.picker().remove();
22331 Roo.apply(Roo.bootstrap.MonthField, {
22350 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22351 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22356 Roo.apply(Roo.bootstrap.MonthField, {
22360 cls: 'datepicker dropdown-menu roo-dynamic',
22364 cls: 'datepicker-months',
22368 cls: 'table-condensed',
22370 Roo.bootstrap.DateField.content
22390 * @class Roo.bootstrap.CheckBox
22391 * @extends Roo.bootstrap.Input
22392 * Bootstrap CheckBox class
22394 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22395 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22396 * @cfg {String} boxLabel The text that appears beside the checkbox
22397 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22398 * @cfg {Boolean} checked initnal the element
22399 * @cfg {Boolean} inline inline the element (default false)
22400 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22401 * @cfg {String} tooltip label tooltip
22404 * Create a new CheckBox
22405 * @param {Object} config The config object
22408 Roo.bootstrap.CheckBox = function(config){
22409 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22414 * Fires when the element is checked or unchecked.
22415 * @param {Roo.bootstrap.CheckBox} this This input
22416 * @param {Boolean} checked The new checked value
22421 * Fires when the element is click.
22422 * @param {Roo.bootstrap.CheckBox} this This input
22429 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22431 inputType: 'checkbox',
22440 // checkbox success does not make any sense really..
22445 getAutoCreate : function()
22447 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22453 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22456 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22462 type : this.inputType,
22463 value : this.inputValue,
22464 cls : 'roo-' + this.inputType, //'form-box',
22465 placeholder : this.placeholder || ''
22469 if(this.inputType != 'radio'){
22473 cls : 'roo-hidden-value',
22474 value : this.checked ? this.inputValue : this.valueOff
22479 if (this.weight) { // Validity check?
22480 cfg.cls += " " + this.inputType + "-" + this.weight;
22483 if (this.disabled) {
22484 input.disabled=true;
22488 input.checked = this.checked;
22493 input.name = this.name;
22495 if(this.inputType != 'radio'){
22496 hidden.name = this.name;
22497 input.name = '_hidden_' + this.name;
22502 input.cls += ' input-' + this.size;
22507 ['xs','sm','md','lg'].map(function(size){
22508 if (settings[size]) {
22509 cfg.cls += ' col-' + size + '-' + settings[size];
22513 var inputblock = input;
22515 if (this.before || this.after) {
22518 cls : 'input-group',
22523 inputblock.cn.push({
22525 cls : 'input-group-addon',
22530 inputblock.cn.push(input);
22532 if(this.inputType != 'radio'){
22533 inputblock.cn.push(hidden);
22537 inputblock.cn.push({
22539 cls : 'input-group-addon',
22545 var boxLabelCfg = false;
22551 //'for': id, // box label is handled by onclick - so no for...
22553 html: this.boxLabel
22556 boxLabelCfg.tooltip = this.tooltip;
22562 if (align ==='left' && this.fieldLabel.length) {
22563 // Roo.log("left and has label");
22568 cls : 'control-label',
22569 html : this.fieldLabel
22580 cfg.cn[1].cn.push(boxLabelCfg);
22583 if(this.labelWidth > 12){
22584 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22587 if(this.labelWidth < 13 && this.labelmd == 0){
22588 this.labelmd = this.labelWidth;
22591 if(this.labellg > 0){
22592 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22593 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22596 if(this.labelmd > 0){
22597 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22598 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22601 if(this.labelsm > 0){
22602 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22603 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22606 if(this.labelxs > 0){
22607 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22608 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22611 } else if ( this.fieldLabel.length) {
22612 // Roo.log(" label");
22616 tag: this.boxLabel ? 'span' : 'label',
22618 cls: 'control-label box-input-label',
22619 //cls : 'input-group-addon',
22620 html : this.fieldLabel
22627 cfg.cn.push(boxLabelCfg);
22632 // Roo.log(" no label && no align");
22633 cfg.cn = [ inputblock ] ;
22635 cfg.cn.push(boxLabelCfg);
22643 if(this.inputType != 'radio'){
22644 cfg.cn.push(hidden);
22652 * return the real input element.
22654 inputEl: function ()
22656 return this.el.select('input.roo-' + this.inputType,true).first();
22658 hiddenEl: function ()
22660 return this.el.select('input.roo-hidden-value',true).first();
22663 labelEl: function()
22665 return this.el.select('label.control-label',true).first();
22667 /* depricated... */
22671 return this.labelEl();
22674 boxLabelEl: function()
22676 return this.el.select('label.box-label',true).first();
22679 initEvents : function()
22681 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22683 this.inputEl().on('click', this.onClick, this);
22685 if (this.boxLabel) {
22686 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22689 this.startValue = this.getValue();
22692 Roo.bootstrap.CheckBox.register(this);
22696 onClick : function(e)
22698 if(this.fireEvent('click', this, e) !== false){
22699 this.setChecked(!this.checked);
22704 setChecked : function(state,suppressEvent)
22706 this.startValue = this.getValue();
22708 if(this.inputType == 'radio'){
22710 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22711 e.dom.checked = false;
22714 this.inputEl().dom.checked = true;
22716 this.inputEl().dom.value = this.inputValue;
22718 if(suppressEvent !== true){
22719 this.fireEvent('check', this, true);
22727 this.checked = state;
22729 this.inputEl().dom.checked = state;
22732 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22734 if(suppressEvent !== true){
22735 this.fireEvent('check', this, state);
22741 getValue : function()
22743 if(this.inputType == 'radio'){
22744 return this.getGroupValue();
22747 return this.hiddenEl().dom.value;
22751 getGroupValue : function()
22753 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22757 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22760 setValue : function(v,suppressEvent)
22762 if(this.inputType == 'radio'){
22763 this.setGroupValue(v, suppressEvent);
22767 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22772 setGroupValue : function(v, suppressEvent)
22774 this.startValue = this.getValue();
22776 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22777 e.dom.checked = false;
22779 if(e.dom.value == v){
22780 e.dom.checked = true;
22784 if(suppressEvent !== true){
22785 this.fireEvent('check', this, true);
22793 validate : function()
22795 if(this.getVisibilityEl().hasClass('hidden')){
22801 (this.inputType == 'radio' && this.validateRadio()) ||
22802 (this.inputType == 'checkbox' && this.validateCheckbox())
22808 this.markInvalid();
22812 validateRadio : function()
22814 if(this.getVisibilityEl().hasClass('hidden')){
22818 if(this.allowBlank){
22824 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22825 if(!e.dom.checked){
22837 validateCheckbox : function()
22840 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22841 //return (this.getValue() == this.inputValue) ? true : false;
22844 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22852 for(var i in group){
22853 if(group[i].el.isVisible(true)){
22861 for(var i in group){
22866 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22873 * Mark this field as valid
22875 markValid : function()
22879 this.fireEvent('valid', this);
22881 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22884 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22891 if(this.inputType == 'radio'){
22892 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22893 var fg = e.findParent('.form-group', false, true);
22894 if (Roo.bootstrap.version == 3) {
22895 fg.removeClass([_this.invalidClass, _this.validClass]);
22896 fg.addClass(_this.validClass);
22898 fg.removeClass(['is-valid', 'is-invalid']);
22899 fg.addClass('is-valid');
22907 var fg = this.el.findParent('.form-group', false, true);
22908 if (Roo.bootstrap.version == 3) {
22909 fg.removeClass([this.invalidClass, this.validClass]);
22910 fg.addClass(this.validClass);
22912 fg.removeClass(['is-valid', 'is-invalid']);
22913 fg.addClass('is-valid');
22918 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22924 for(var i in group){
22925 var fg = group[i].el.findParent('.form-group', false, true);
22926 if (Roo.bootstrap.version == 3) {
22927 fg.removeClass([this.invalidClass, this.validClass]);
22928 fg.addClass(this.validClass);
22930 fg.removeClass(['is-valid', 'is-invalid']);
22931 fg.addClass('is-valid');
22937 * Mark this field as invalid
22938 * @param {String} msg The validation message
22940 markInvalid : function(msg)
22942 if(this.allowBlank){
22948 this.fireEvent('invalid', this, msg);
22950 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22953 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22957 label.markInvalid();
22960 if(this.inputType == 'radio'){
22962 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22963 var fg = e.findParent('.form-group', false, true);
22964 if (Roo.bootstrap.version == 3) {
22965 fg.removeClass([_this.invalidClass, _this.validClass]);
22966 fg.addClass(_this.invalidClass);
22968 fg.removeClass(['is-invalid', 'is-valid']);
22969 fg.addClass('is-invalid');
22977 var fg = this.el.findParent('.form-group', false, true);
22978 if (Roo.bootstrap.version == 3) {
22979 fg.removeClass([_this.invalidClass, _this.validClass]);
22980 fg.addClass(_this.invalidClass);
22982 fg.removeClass(['is-invalid', 'is-valid']);
22983 fg.addClass('is-invalid');
22988 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22994 for(var i in group){
22995 var fg = group[i].el.findParent('.form-group', false, true);
22996 if (Roo.bootstrap.version == 3) {
22997 fg.removeClass([_this.invalidClass, _this.validClass]);
22998 fg.addClass(_this.invalidClass);
23000 fg.removeClass(['is-invalid', 'is-valid']);
23001 fg.addClass('is-invalid');
23007 clearInvalid : function()
23009 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23011 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23013 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23015 if (label && label.iconEl) {
23016 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23017 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23021 disable : function()
23023 if(this.inputType != 'radio'){
23024 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23031 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23032 _this.getActionEl().addClass(this.disabledClass);
23033 e.dom.disabled = true;
23037 this.disabled = true;
23038 this.fireEvent("disable", this);
23042 enable : function()
23044 if(this.inputType != 'radio'){
23045 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23052 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23053 _this.getActionEl().removeClass(this.disabledClass);
23054 e.dom.disabled = false;
23058 this.disabled = false;
23059 this.fireEvent("enable", this);
23063 setBoxLabel : function(v)
23068 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23074 Roo.apply(Roo.bootstrap.CheckBox, {
23079 * register a CheckBox Group
23080 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23082 register : function(checkbox)
23084 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23085 this.groups[checkbox.groupId] = {};
23088 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23092 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23096 * fetch a CheckBox Group based on the group ID
23097 * @param {string} the group ID
23098 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23100 get: function(groupId) {
23101 if (typeof(this.groups[groupId]) == 'undefined') {
23105 return this.groups[groupId] ;
23118 * @class Roo.bootstrap.Radio
23119 * @extends Roo.bootstrap.Component
23120 * Bootstrap Radio class
23121 * @cfg {String} boxLabel - the label associated
23122 * @cfg {String} value - the value of radio
23125 * Create a new Radio
23126 * @param {Object} config The config object
23128 Roo.bootstrap.Radio = function(config){
23129 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23133 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23139 getAutoCreate : function()
23143 cls : 'form-group radio',
23148 html : this.boxLabel
23156 initEvents : function()
23158 this.parent().register(this);
23160 this.el.on('click', this.onClick, this);
23164 onClick : function(e)
23166 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23167 this.setChecked(true);
23171 setChecked : function(state, suppressEvent)
23173 this.parent().setValue(this.value, suppressEvent);
23177 setBoxLabel : function(v)
23182 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23197 * @class Roo.bootstrap.SecurePass
23198 * @extends Roo.bootstrap.Input
23199 * Bootstrap SecurePass class
23203 * Create a new SecurePass
23204 * @param {Object} config The config object
23207 Roo.bootstrap.SecurePass = function (config) {
23208 // these go here, so the translation tool can replace them..
23210 PwdEmpty: "Please type a password, and then retype it to confirm.",
23211 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23212 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23213 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23214 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23215 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23216 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23217 TooWeak: "Your password is Too Weak."
23219 this.meterLabel = "Password strength:";
23220 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23221 this.meterClass = [
23222 "roo-password-meter-tooweak",
23223 "roo-password-meter-weak",
23224 "roo-password-meter-medium",
23225 "roo-password-meter-strong",
23226 "roo-password-meter-grey"
23231 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23234 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23236 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23238 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23239 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23240 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23241 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23242 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23243 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23244 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23254 * @cfg {String/Object} Label for the strength meter (defaults to
23255 * 'Password strength:')
23260 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23261 * ['Weak', 'Medium', 'Strong'])
23264 pwdStrengths: false,
23277 initEvents: function ()
23279 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23281 if (this.el.is('input[type=password]') && Roo.isSafari) {
23282 this.el.on('keydown', this.SafariOnKeyDown, this);
23285 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23288 onRender: function (ct, position)
23290 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23291 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23292 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23294 this.trigger.createChild({
23299 cls: 'roo-password-meter-grey col-xs-12',
23302 //width: this.meterWidth + 'px'
23306 cls: 'roo-password-meter-text'
23312 if (this.hideTrigger) {
23313 this.trigger.setDisplayed(false);
23315 this.setSize(this.width || '', this.height || '');
23318 onDestroy: function ()
23320 if (this.trigger) {
23321 this.trigger.removeAllListeners();
23322 this.trigger.remove();
23325 this.wrap.remove();
23327 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23330 checkStrength: function ()
23332 var pwd = this.inputEl().getValue();
23333 if (pwd == this._lastPwd) {
23338 if (this.ClientSideStrongPassword(pwd)) {
23340 } else if (this.ClientSideMediumPassword(pwd)) {
23342 } else if (this.ClientSideWeakPassword(pwd)) {
23348 Roo.log('strength1: ' + strength);
23350 //var pm = this.trigger.child('div/div/div').dom;
23351 var pm = this.trigger.child('div/div');
23352 pm.removeClass(this.meterClass);
23353 pm.addClass(this.meterClass[strength]);
23356 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23358 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23360 this._lastPwd = pwd;
23364 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23366 this._lastPwd = '';
23368 var pm = this.trigger.child('div/div');
23369 pm.removeClass(this.meterClass);
23370 pm.addClass('roo-password-meter-grey');
23373 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23376 this.inputEl().dom.type='password';
23379 validateValue: function (value)
23382 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23385 if (value.length == 0) {
23386 if (this.allowBlank) {
23387 this.clearInvalid();
23391 this.markInvalid(this.errors.PwdEmpty);
23392 this.errorMsg = this.errors.PwdEmpty;
23400 if ('[\x21-\x7e]*'.match(value)) {
23401 this.markInvalid(this.errors.PwdBadChar);
23402 this.errorMsg = this.errors.PwdBadChar;
23405 if (value.length < 6) {
23406 this.markInvalid(this.errors.PwdShort);
23407 this.errorMsg = this.errors.PwdShort;
23410 if (value.length > 16) {
23411 this.markInvalid(this.errors.PwdLong);
23412 this.errorMsg = this.errors.PwdLong;
23416 if (this.ClientSideStrongPassword(value)) {
23418 } else if (this.ClientSideMediumPassword(value)) {
23420 } else if (this.ClientSideWeakPassword(value)) {
23427 if (strength < 2) {
23428 //this.markInvalid(this.errors.TooWeak);
23429 this.errorMsg = this.errors.TooWeak;
23434 console.log('strength2: ' + strength);
23436 //var pm = this.trigger.child('div/div/div').dom;
23438 var pm = this.trigger.child('div/div');
23439 pm.removeClass(this.meterClass);
23440 pm.addClass(this.meterClass[strength]);
23442 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23444 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23446 this.errorMsg = '';
23450 CharacterSetChecks: function (type)
23453 this.fResult = false;
23456 isctype: function (character, type)
23459 case this.kCapitalLetter:
23460 if (character >= 'A' && character <= 'Z') {
23465 case this.kSmallLetter:
23466 if (character >= 'a' && character <= 'z') {
23472 if (character >= '0' && character <= '9') {
23477 case this.kPunctuation:
23478 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23489 IsLongEnough: function (pwd, size)
23491 return !(pwd == null || isNaN(size) || pwd.length < size);
23494 SpansEnoughCharacterSets: function (word, nb)
23496 if (!this.IsLongEnough(word, nb))
23501 var characterSetChecks = new Array(
23502 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23503 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23506 for (var index = 0; index < word.length; ++index) {
23507 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23508 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23509 characterSetChecks[nCharSet].fResult = true;
23516 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23517 if (characterSetChecks[nCharSet].fResult) {
23522 if (nCharSets < nb) {
23528 ClientSideStrongPassword: function (pwd)
23530 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23533 ClientSideMediumPassword: function (pwd)
23535 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23538 ClientSideWeakPassword: function (pwd)
23540 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23543 })//<script type="text/javascript">
23546 * Based Ext JS Library 1.1.1
23547 * Copyright(c) 2006-2007, Ext JS, LLC.
23553 * @class Roo.HtmlEditorCore
23554 * @extends Roo.Component
23555 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23557 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23560 Roo.HtmlEditorCore = function(config){
23563 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23568 * @event initialize
23569 * Fires when the editor is fully initialized (including the iframe)
23570 * @param {Roo.HtmlEditorCore} this
23575 * Fires when the editor is first receives the focus. Any insertion must wait
23576 * until after this event.
23577 * @param {Roo.HtmlEditorCore} this
23581 * @event beforesync
23582 * Fires before the textarea is updated with content from the editor iframe. Return false
23583 * to cancel the sync.
23584 * @param {Roo.HtmlEditorCore} this
23585 * @param {String} html
23589 * @event beforepush
23590 * Fires before the iframe editor is updated with content from the textarea. Return false
23591 * to cancel the push.
23592 * @param {Roo.HtmlEditorCore} this
23593 * @param {String} html
23598 * Fires when the textarea is updated with content from the editor iframe.
23599 * @param {Roo.HtmlEditorCore} this
23600 * @param {String} html
23605 * Fires when the iframe editor is updated with content from the textarea.
23606 * @param {Roo.HtmlEditorCore} this
23607 * @param {String} html
23612 * @event editorevent
23613 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23614 * @param {Roo.HtmlEditorCore} this
23620 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23622 // defaults : white / black...
23623 this.applyBlacklists();
23630 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23634 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23640 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23645 * @cfg {Number} height (in pixels)
23649 * @cfg {Number} width (in pixels)
23654 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23657 stylesheets: false,
23662 // private properties
23663 validationEvent : false,
23665 initialized : false,
23667 sourceEditMode : false,
23668 onFocus : Roo.emptyFn,
23670 hideMode:'offsets',
23674 // blacklist + whitelisted elements..
23681 * Protected method that will not generally be called directly. It
23682 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23683 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23685 getDocMarkup : function(){
23689 // inherit styels from page...??
23690 if (this.stylesheets === false) {
23692 Roo.get(document.head).select('style').each(function(node) {
23693 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23696 Roo.get(document.head).select('link').each(function(node) {
23697 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23700 } else if (!this.stylesheets.length) {
23702 st = '<style type="text/css">' +
23703 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23706 st = '<style type="text/css">' +
23711 st += '<style type="text/css">' +
23712 'IMG { cursor: pointer } ' +
23715 var cls = 'roo-htmleditor-body';
23717 if(this.bodyCls.length){
23718 cls += ' ' + this.bodyCls;
23721 return '<html><head>' + st +
23722 //<style type="text/css">' +
23723 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23725 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23729 onRender : function(ct, position)
23732 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23733 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23736 this.el.dom.style.border = '0 none';
23737 this.el.dom.setAttribute('tabIndex', -1);
23738 this.el.addClass('x-hidden hide');
23742 if(Roo.isIE){ // fix IE 1px bogus margin
23743 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23747 this.frameId = Roo.id();
23751 var iframe = this.owner.wrap.createChild({
23753 cls: 'form-control', // bootstrap..
23755 name: this.frameId,
23756 frameBorder : 'no',
23757 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23762 this.iframe = iframe.dom;
23764 this.assignDocWin();
23766 this.doc.designMode = 'on';
23769 this.doc.write(this.getDocMarkup());
23773 var task = { // must defer to wait for browser to be ready
23775 //console.log("run task?" + this.doc.readyState);
23776 this.assignDocWin();
23777 if(this.doc.body || this.doc.readyState == 'complete'){
23779 this.doc.designMode="on";
23783 Roo.TaskMgr.stop(task);
23784 this.initEditor.defer(10, this);
23791 Roo.TaskMgr.start(task);
23796 onResize : function(w, h)
23798 Roo.log('resize: ' +w + ',' + h );
23799 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23803 if(typeof w == 'number'){
23805 this.iframe.style.width = w + 'px';
23807 if(typeof h == 'number'){
23809 this.iframe.style.height = h + 'px';
23811 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23818 * Toggles the editor between standard and source edit mode.
23819 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23821 toggleSourceEdit : function(sourceEditMode){
23823 this.sourceEditMode = sourceEditMode === true;
23825 if(this.sourceEditMode){
23827 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23830 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23831 //this.iframe.className = '';
23834 //this.setSize(this.owner.wrap.getSize());
23835 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23842 * Protected method that will not generally be called directly. If you need/want
23843 * custom HTML cleanup, this is the method you should override.
23844 * @param {String} html The HTML to be cleaned
23845 * return {String} The cleaned HTML
23847 cleanHtml : function(html){
23848 html = String(html);
23849 if(html.length > 5){
23850 if(Roo.isSafari){ // strip safari nonsense
23851 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23854 if(html == ' '){
23861 * HTML Editor -> Textarea
23862 * Protected method that will not generally be called directly. Syncs the contents
23863 * of the editor iframe with the textarea.
23865 syncValue : function(){
23866 if(this.initialized){
23867 var bd = (this.doc.body || this.doc.documentElement);
23868 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23869 var html = bd.innerHTML;
23871 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23872 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23874 html = '<div style="'+m[0]+'">' + html + '</div>';
23877 html = this.cleanHtml(html);
23878 // fix up the special chars.. normaly like back quotes in word...
23879 // however we do not want to do this with chinese..
23880 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23882 var cc = match.charCodeAt();
23884 // Get the character value, handling surrogate pairs
23885 if (match.length == 2) {
23886 // It's a surrogate pair, calculate the Unicode code point
23887 var high = match.charCodeAt(0) - 0xD800;
23888 var low = match.charCodeAt(1) - 0xDC00;
23889 cc = (high * 0x400) + low + 0x10000;
23891 (cc >= 0x4E00 && cc < 0xA000 ) ||
23892 (cc >= 0x3400 && cc < 0x4E00 ) ||
23893 (cc >= 0xf900 && cc < 0xfb00 )
23898 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23899 return "&#" + cc + ";";
23906 if(this.owner.fireEvent('beforesync', this, html) !== false){
23907 this.el.dom.value = html;
23908 this.owner.fireEvent('sync', this, html);
23914 * Protected method that will not generally be called directly. Pushes the value of the textarea
23915 * into the iframe editor.
23917 pushValue : function(){
23918 if(this.initialized){
23919 var v = this.el.dom.value.trim();
23921 // if(v.length < 1){
23925 if(this.owner.fireEvent('beforepush', this, v) !== false){
23926 var d = (this.doc.body || this.doc.documentElement);
23928 this.cleanUpPaste();
23929 this.el.dom.value = d.innerHTML;
23930 this.owner.fireEvent('push', this, v);
23936 deferFocus : function(){
23937 this.focus.defer(10, this);
23941 focus : function(){
23942 if(this.win && !this.sourceEditMode){
23949 assignDocWin: function()
23951 var iframe = this.iframe;
23954 this.doc = iframe.contentWindow.document;
23955 this.win = iframe.contentWindow;
23957 // if (!Roo.get(this.frameId)) {
23960 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23961 // this.win = Roo.get(this.frameId).dom.contentWindow;
23963 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23967 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23968 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23973 initEditor : function(){
23974 //console.log("INIT EDITOR");
23975 this.assignDocWin();
23979 this.doc.designMode="on";
23981 this.doc.write(this.getDocMarkup());
23984 var dbody = (this.doc.body || this.doc.documentElement);
23985 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23986 // this copies styles from the containing element into thsi one..
23987 // not sure why we need all of this..
23988 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23990 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23991 //ss['background-attachment'] = 'fixed'; // w3c
23992 dbody.bgProperties = 'fixed'; // ie
23993 //Roo.DomHelper.applyStyles(dbody, ss);
23994 Roo.EventManager.on(this.doc, {
23995 //'mousedown': this.onEditorEvent,
23996 'mouseup': this.onEditorEvent,
23997 'dblclick': this.onEditorEvent,
23998 'click': this.onEditorEvent,
23999 'keyup': this.onEditorEvent,
24004 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24006 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24007 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24009 this.initialized = true;
24011 this.owner.fireEvent('initialize', this);
24016 onDestroy : function(){
24022 //for (var i =0; i < this.toolbars.length;i++) {
24023 // // fixme - ask toolbars for heights?
24024 // this.toolbars[i].onDestroy();
24027 //this.wrap.dom.innerHTML = '';
24028 //this.wrap.remove();
24033 onFirstFocus : function(){
24035 this.assignDocWin();
24038 this.activated = true;
24041 if(Roo.isGecko){ // prevent silly gecko errors
24043 var s = this.win.getSelection();
24044 if(!s.focusNode || s.focusNode.nodeType != 3){
24045 var r = s.getRangeAt(0);
24046 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24051 this.execCmd('useCSS', true);
24052 this.execCmd('styleWithCSS', false);
24055 this.owner.fireEvent('activate', this);
24059 adjustFont: function(btn){
24060 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24061 //if(Roo.isSafari){ // safari
24064 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24065 if(Roo.isSafari){ // safari
24066 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24067 v = (v < 10) ? 10 : v;
24068 v = (v > 48) ? 48 : v;
24069 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24074 v = Math.max(1, v+adjust);
24076 this.execCmd('FontSize', v );
24079 onEditorEvent : function(e)
24081 this.owner.fireEvent('editorevent', this, e);
24082 // this.updateToolbar();
24083 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24086 insertTag : function(tg)
24088 // could be a bit smarter... -> wrap the current selected tRoo..
24089 if (tg.toLowerCase() == 'span' ||
24090 tg.toLowerCase() == 'code' ||
24091 tg.toLowerCase() == 'sup' ||
24092 tg.toLowerCase() == 'sub'
24095 range = this.createRange(this.getSelection());
24096 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24097 wrappingNode.appendChild(range.extractContents());
24098 range.insertNode(wrappingNode);
24105 this.execCmd("formatblock", tg);
24109 insertText : function(txt)
24113 var range = this.createRange();
24114 range.deleteContents();
24115 //alert(Sender.getAttribute('label'));
24117 range.insertNode(this.doc.createTextNode(txt));
24123 * Executes a Midas editor command on the editor document and performs necessary focus and
24124 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24125 * @param {String} cmd The Midas command
24126 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24128 relayCmd : function(cmd, value){
24130 this.execCmd(cmd, value);
24131 this.owner.fireEvent('editorevent', this);
24132 //this.updateToolbar();
24133 this.owner.deferFocus();
24137 * Executes a Midas editor command directly on the editor document.
24138 * For visual commands, you should use {@link #relayCmd} instead.
24139 * <b>This should only be called after the editor is initialized.</b>
24140 * @param {String} cmd The Midas command
24141 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24143 execCmd : function(cmd, value){
24144 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24151 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24153 * @param {String} text | dom node..
24155 insertAtCursor : function(text)
24158 if(!this.activated){
24164 var r = this.doc.selection.createRange();
24175 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24179 // from jquery ui (MIT licenced)
24181 var win = this.win;
24183 if (win.getSelection && win.getSelection().getRangeAt) {
24184 range = win.getSelection().getRangeAt(0);
24185 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24186 range.insertNode(node);
24187 } else if (win.document.selection && win.document.selection.createRange) {
24188 // no firefox support
24189 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24190 win.document.selection.createRange().pasteHTML(txt);
24192 // no firefox support
24193 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24194 this.execCmd('InsertHTML', txt);
24203 mozKeyPress : function(e){
24205 var c = e.getCharCode(), cmd;
24208 c = String.fromCharCode(c).toLowerCase();
24222 this.cleanUpPaste.defer(100, this);
24230 e.preventDefault();
24238 fixKeys : function(){ // load time branching for fastest keydown performance
24240 return function(e){
24241 var k = e.getKey(), r;
24244 r = this.doc.selection.createRange();
24247 r.pasteHTML('    ');
24254 r = this.doc.selection.createRange();
24256 var target = r.parentElement();
24257 if(!target || target.tagName.toLowerCase() != 'li'){
24259 r.pasteHTML('<br />');
24265 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24266 this.cleanUpPaste.defer(100, this);
24272 }else if(Roo.isOpera){
24273 return function(e){
24274 var k = e.getKey();
24278 this.execCmd('InsertHTML','    ');
24281 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24282 this.cleanUpPaste.defer(100, this);
24287 }else if(Roo.isSafari){
24288 return function(e){
24289 var k = e.getKey();
24293 this.execCmd('InsertText','\t');
24297 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24298 this.cleanUpPaste.defer(100, this);
24306 getAllAncestors: function()
24308 var p = this.getSelectedNode();
24311 a.push(p); // push blank onto stack..
24312 p = this.getParentElement();
24316 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24320 a.push(this.doc.body);
24324 lastSelNode : false,
24327 getSelection : function()
24329 this.assignDocWin();
24330 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24333 getSelectedNode: function()
24335 // this may only work on Gecko!!!
24337 // should we cache this!!!!
24342 var range = this.createRange(this.getSelection()).cloneRange();
24345 var parent = range.parentElement();
24347 var testRange = range.duplicate();
24348 testRange.moveToElementText(parent);
24349 if (testRange.inRange(range)) {
24352 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24355 parent = parent.parentElement;
24360 // is ancestor a text element.
24361 var ac = range.commonAncestorContainer;
24362 if (ac.nodeType == 3) {
24363 ac = ac.parentNode;
24366 var ar = ac.childNodes;
24369 var other_nodes = [];
24370 var has_other_nodes = false;
24371 for (var i=0;i<ar.length;i++) {
24372 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24375 // fullly contained node.
24377 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24382 // probably selected..
24383 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24384 other_nodes.push(ar[i]);
24388 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24393 has_other_nodes = true;
24395 if (!nodes.length && other_nodes.length) {
24396 nodes= other_nodes;
24398 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24404 createRange: function(sel)
24406 // this has strange effects when using with
24407 // top toolbar - not sure if it's a great idea.
24408 //this.editor.contentWindow.focus();
24409 if (typeof sel != "undefined") {
24411 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24413 return this.doc.createRange();
24416 return this.doc.createRange();
24419 getParentElement: function()
24422 this.assignDocWin();
24423 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24425 var range = this.createRange(sel);
24428 var p = range.commonAncestorContainer;
24429 while (p.nodeType == 3) { // text node
24440 * Range intersection.. the hard stuff...
24444 * [ -- selected range --- ]
24448 * if end is before start or hits it. fail.
24449 * if start is after end or hits it fail.
24451 * if either hits (but other is outside. - then it's not
24457 // @see http://www.thismuchiknow.co.uk/?p=64.
24458 rangeIntersectsNode : function(range, node)
24460 var nodeRange = node.ownerDocument.createRange();
24462 nodeRange.selectNode(node);
24464 nodeRange.selectNodeContents(node);
24467 var rangeStartRange = range.cloneRange();
24468 rangeStartRange.collapse(true);
24470 var rangeEndRange = range.cloneRange();
24471 rangeEndRange.collapse(false);
24473 var nodeStartRange = nodeRange.cloneRange();
24474 nodeStartRange.collapse(true);
24476 var nodeEndRange = nodeRange.cloneRange();
24477 nodeEndRange.collapse(false);
24479 return rangeStartRange.compareBoundaryPoints(
24480 Range.START_TO_START, nodeEndRange) == -1 &&
24481 rangeEndRange.compareBoundaryPoints(
24482 Range.START_TO_START, nodeStartRange) == 1;
24486 rangeCompareNode : function(range, node)
24488 var nodeRange = node.ownerDocument.createRange();
24490 nodeRange.selectNode(node);
24492 nodeRange.selectNodeContents(node);
24496 range.collapse(true);
24498 nodeRange.collapse(true);
24500 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24501 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24503 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24505 var nodeIsBefore = ss == 1;
24506 var nodeIsAfter = ee == -1;
24508 if (nodeIsBefore && nodeIsAfter) {
24511 if (!nodeIsBefore && nodeIsAfter) {
24512 return 1; //right trailed.
24515 if (nodeIsBefore && !nodeIsAfter) {
24516 return 2; // left trailed.
24522 // private? - in a new class?
24523 cleanUpPaste : function()
24525 // cleans up the whole document..
24526 Roo.log('cleanuppaste');
24528 this.cleanUpChildren(this.doc.body);
24529 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24530 if (clean != this.doc.body.innerHTML) {
24531 this.doc.body.innerHTML = clean;
24536 cleanWordChars : function(input) {// change the chars to hex code
24537 var he = Roo.HtmlEditorCore;
24539 var output = input;
24540 Roo.each(he.swapCodes, function(sw) {
24541 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24543 output = output.replace(swapper, sw[1]);
24550 cleanUpChildren : function (n)
24552 if (!n.childNodes.length) {
24555 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24556 this.cleanUpChild(n.childNodes[i]);
24563 cleanUpChild : function (node)
24566 //console.log(node);
24567 if (node.nodeName == "#text") {
24568 // clean up silly Windows -- stuff?
24571 if (node.nodeName == "#comment") {
24572 node.parentNode.removeChild(node);
24573 // clean up silly Windows -- stuff?
24576 var lcname = node.tagName.toLowerCase();
24577 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24578 // whitelist of tags..
24580 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24582 node.parentNode.removeChild(node);
24587 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24589 // spans with no attributes - just remove them..
24590 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24591 remove_keep_children = true;
24594 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24595 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24597 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24598 // remove_keep_children = true;
24601 if (remove_keep_children) {
24602 this.cleanUpChildren(node);
24603 // inserts everything just before this node...
24604 while (node.childNodes.length) {
24605 var cn = node.childNodes[0];
24606 node.removeChild(cn);
24607 node.parentNode.insertBefore(cn, node);
24609 node.parentNode.removeChild(node);
24613 if (!node.attributes || !node.attributes.length) {
24618 this.cleanUpChildren(node);
24622 function cleanAttr(n,v)
24625 if (v.match(/^\./) || v.match(/^\//)) {
24628 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24631 if (v.match(/^#/)) {
24634 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24635 node.removeAttribute(n);
24639 var cwhite = this.cwhite;
24640 var cblack = this.cblack;
24642 function cleanStyle(n,v)
24644 if (v.match(/expression/)) { //XSS?? should we even bother..
24645 node.removeAttribute(n);
24649 var parts = v.split(/;/);
24652 Roo.each(parts, function(p) {
24653 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24657 var l = p.split(':').shift().replace(/\s+/g,'');
24658 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24660 if ( cwhite.length && cblack.indexOf(l) > -1) {
24661 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24662 //node.removeAttribute(n);
24666 // only allow 'c whitelisted system attributes'
24667 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24668 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24669 //node.removeAttribute(n);
24679 if (clean.length) {
24680 node.setAttribute(n, clean.join(';'));
24682 node.removeAttribute(n);
24688 for (var i = node.attributes.length-1; i > -1 ; i--) {
24689 var a = node.attributes[i];
24692 if (a.name.toLowerCase().substr(0,2)=='on') {
24693 node.removeAttribute(a.name);
24696 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24697 node.removeAttribute(a.name);
24700 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24701 cleanAttr(a.name,a.value); // fixme..
24704 if (a.name == 'style') {
24705 cleanStyle(a.name,a.value);
24708 /// clean up MS crap..
24709 // tecnically this should be a list of valid class'es..
24712 if (a.name == 'class') {
24713 if (a.value.match(/^Mso/)) {
24714 node.removeAttribute('class');
24717 if (a.value.match(/^body$/)) {
24718 node.removeAttribute('class');
24729 this.cleanUpChildren(node);
24735 * Clean up MS wordisms...
24737 cleanWord : function(node)
24740 this.cleanWord(this.doc.body);
24745 node.nodeName == 'SPAN' &&
24746 !node.hasAttributes() &&
24747 node.childNodes.length == 1 &&
24748 node.firstChild.nodeName == "#text"
24750 var textNode = node.firstChild;
24751 node.removeChild(textNode);
24752 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24753 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24755 node.parentNode.insertBefore(textNode, node);
24756 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24757 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24759 node.parentNode.removeChild(node);
24762 if (node.nodeName == "#text") {
24763 // clean up silly Windows -- stuff?
24766 if (node.nodeName == "#comment") {
24767 node.parentNode.removeChild(node);
24768 // clean up silly Windows -- stuff?
24772 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24773 node.parentNode.removeChild(node);
24776 //Roo.log(node.tagName);
24777 // remove - but keep children..
24778 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24779 //Roo.log('-- removed');
24780 while (node.childNodes.length) {
24781 var cn = node.childNodes[0];
24782 node.removeChild(cn);
24783 node.parentNode.insertBefore(cn, node);
24784 // move node to parent - and clean it..
24785 this.cleanWord(cn);
24787 node.parentNode.removeChild(node);
24788 /// no need to iterate chidlren = it's got none..
24789 //this.iterateChildren(node, this.cleanWord);
24793 if (node.className.length) {
24795 var cn = node.className.split(/\W+/);
24797 Roo.each(cn, function(cls) {
24798 if (cls.match(/Mso[a-zA-Z]+/)) {
24803 node.className = cna.length ? cna.join(' ') : '';
24805 node.removeAttribute("class");
24809 if (node.hasAttribute("lang")) {
24810 node.removeAttribute("lang");
24813 if (node.hasAttribute("style")) {
24815 var styles = node.getAttribute("style").split(";");
24817 Roo.each(styles, function(s) {
24818 if (!s.match(/:/)) {
24821 var kv = s.split(":");
24822 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24825 // what ever is left... we allow.
24828 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24829 if (!nstyle.length) {
24830 node.removeAttribute('style');
24833 this.iterateChildren(node, this.cleanWord);
24839 * iterateChildren of a Node, calling fn each time, using this as the scole..
24840 * @param {DomNode} node node to iterate children of.
24841 * @param {Function} fn method of this class to call on each item.
24843 iterateChildren : function(node, fn)
24845 if (!node.childNodes.length) {
24848 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24849 fn.call(this, node.childNodes[i])
24855 * cleanTableWidths.
24857 * Quite often pasting from word etc.. results in tables with column and widths.
24858 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24861 cleanTableWidths : function(node)
24866 this.cleanTableWidths(this.doc.body);
24871 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24874 Roo.log(node.tagName);
24875 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24876 this.iterateChildren(node, this.cleanTableWidths);
24879 if (node.hasAttribute('width')) {
24880 node.removeAttribute('width');
24884 if (node.hasAttribute("style")) {
24887 var styles = node.getAttribute("style").split(";");
24889 Roo.each(styles, function(s) {
24890 if (!s.match(/:/)) {
24893 var kv = s.split(":");
24894 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24897 // what ever is left... we allow.
24900 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24901 if (!nstyle.length) {
24902 node.removeAttribute('style');
24906 this.iterateChildren(node, this.cleanTableWidths);
24914 domToHTML : function(currentElement, depth, nopadtext) {
24916 depth = depth || 0;
24917 nopadtext = nopadtext || false;
24919 if (!currentElement) {
24920 return this.domToHTML(this.doc.body);
24923 //Roo.log(currentElement);
24925 var allText = false;
24926 var nodeName = currentElement.nodeName;
24927 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24929 if (nodeName == '#text') {
24931 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24936 if (nodeName != 'BODY') {
24939 // Prints the node tagName, such as <A>, <IMG>, etc
24942 for(i = 0; i < currentElement.attributes.length;i++) {
24944 var aname = currentElement.attributes.item(i).name;
24945 if (!currentElement.attributes.item(i).value.length) {
24948 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24951 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24960 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24963 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24968 // Traverse the tree
24970 var currentElementChild = currentElement.childNodes.item(i);
24971 var allText = true;
24972 var innerHTML = '';
24974 while (currentElementChild) {
24975 // Formatting code (indent the tree so it looks nice on the screen)
24976 var nopad = nopadtext;
24977 if (lastnode == 'SPAN') {
24981 if (currentElementChild.nodeName == '#text') {
24982 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24983 toadd = nopadtext ? toadd : toadd.trim();
24984 if (!nopad && toadd.length > 80) {
24985 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
24987 innerHTML += toadd;
24990 currentElementChild = currentElement.childNodes.item(i);
24996 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
24998 // Recursively traverse the tree structure of the child node
24999 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25000 lastnode = currentElementChild.nodeName;
25002 currentElementChild=currentElement.childNodes.item(i);
25008 // The remaining code is mostly for formatting the tree
25009 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25014 ret+= "</"+tagName+">";
25020 applyBlacklists : function()
25022 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25023 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25027 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25028 if (b.indexOf(tag) > -1) {
25031 this.white.push(tag);
25035 Roo.each(w, function(tag) {
25036 if (b.indexOf(tag) > -1) {
25039 if (this.white.indexOf(tag) > -1) {
25042 this.white.push(tag);
25047 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25048 if (w.indexOf(tag) > -1) {
25051 this.black.push(tag);
25055 Roo.each(b, function(tag) {
25056 if (w.indexOf(tag) > -1) {
25059 if (this.black.indexOf(tag) > -1) {
25062 this.black.push(tag);
25067 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25068 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25072 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25073 if (b.indexOf(tag) > -1) {
25076 this.cwhite.push(tag);
25080 Roo.each(w, function(tag) {
25081 if (b.indexOf(tag) > -1) {
25084 if (this.cwhite.indexOf(tag) > -1) {
25087 this.cwhite.push(tag);
25092 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25093 if (w.indexOf(tag) > -1) {
25096 this.cblack.push(tag);
25100 Roo.each(b, function(tag) {
25101 if (w.indexOf(tag) > -1) {
25104 if (this.cblack.indexOf(tag) > -1) {
25107 this.cblack.push(tag);
25112 setStylesheets : function(stylesheets)
25114 if(typeof(stylesheets) == 'string'){
25115 Roo.get(this.iframe.contentDocument.head).createChild({
25117 rel : 'stylesheet',
25126 Roo.each(stylesheets, function(s) {
25131 Roo.get(_this.iframe.contentDocument.head).createChild({
25133 rel : 'stylesheet',
25142 removeStylesheets : function()
25146 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25151 setStyle : function(style)
25153 Roo.get(this.iframe.contentDocument.head).createChild({
25162 // hide stuff that is not compatible
25176 * @event specialkey
25180 * @cfg {String} fieldClass @hide
25183 * @cfg {String} focusClass @hide
25186 * @cfg {String} autoCreate @hide
25189 * @cfg {String} inputType @hide
25192 * @cfg {String} invalidClass @hide
25195 * @cfg {String} invalidText @hide
25198 * @cfg {String} msgFx @hide
25201 * @cfg {String} validateOnBlur @hide
25205 Roo.HtmlEditorCore.white = [
25206 'area', 'br', 'img', 'input', 'hr', 'wbr',
25208 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25209 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25210 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25211 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25212 'table', 'ul', 'xmp',
25214 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25217 'dir', 'menu', 'ol', 'ul', 'dl',
25223 Roo.HtmlEditorCore.black = [
25224 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25226 'base', 'basefont', 'bgsound', 'blink', 'body',
25227 'frame', 'frameset', 'head', 'html', 'ilayer',
25228 'iframe', 'layer', 'link', 'meta', 'object',
25229 'script', 'style' ,'title', 'xml' // clean later..
25231 Roo.HtmlEditorCore.clean = [
25232 'script', 'style', 'title', 'xml'
25234 Roo.HtmlEditorCore.remove = [
25239 Roo.HtmlEditorCore.ablack = [
25243 Roo.HtmlEditorCore.aclean = [
25244 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25248 Roo.HtmlEditorCore.pwhite= [
25249 'http', 'https', 'mailto'
25252 // white listed style attributes.
25253 Roo.HtmlEditorCore.cwhite= [
25254 // 'text-align', /// default is to allow most things..
25260 // black listed style attributes.
25261 Roo.HtmlEditorCore.cblack= [
25262 // 'font-size' -- this can be set by the project
25266 Roo.HtmlEditorCore.swapCodes =[
25285 * @class Roo.bootstrap.HtmlEditor
25286 * @extends Roo.bootstrap.TextArea
25287 * Bootstrap HtmlEditor class
25290 * Create a new HtmlEditor
25291 * @param {Object} config The config object
25294 Roo.bootstrap.HtmlEditor = function(config){
25295 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25296 if (!this.toolbars) {
25297 this.toolbars = [];
25300 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25303 * @event initialize
25304 * Fires when the editor is fully initialized (including the iframe)
25305 * @param {HtmlEditor} this
25310 * Fires when the editor is first receives the focus. Any insertion must wait
25311 * until after this event.
25312 * @param {HtmlEditor} this
25316 * @event beforesync
25317 * Fires before the textarea is updated with content from the editor iframe. Return false
25318 * to cancel the sync.
25319 * @param {HtmlEditor} this
25320 * @param {String} html
25324 * @event beforepush
25325 * Fires before the iframe editor is updated with content from the textarea. Return false
25326 * to cancel the push.
25327 * @param {HtmlEditor} this
25328 * @param {String} html
25333 * Fires when the textarea is updated with content from the editor iframe.
25334 * @param {HtmlEditor} this
25335 * @param {String} html
25340 * Fires when the iframe editor is updated with content from the textarea.
25341 * @param {HtmlEditor} this
25342 * @param {String} html
25346 * @event editmodechange
25347 * Fires when the editor switches edit modes
25348 * @param {HtmlEditor} this
25349 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25351 editmodechange: true,
25353 * @event editorevent
25354 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25355 * @param {HtmlEditor} this
25359 * @event firstfocus
25360 * Fires when on first focus - needed by toolbars..
25361 * @param {HtmlEditor} this
25366 * Auto save the htmlEditor value as a file into Events
25367 * @param {HtmlEditor} this
25371 * @event savedpreview
25372 * preview the saved version of htmlEditor
25373 * @param {HtmlEditor} this
25380 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25384 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25389 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25394 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25399 * @cfg {Number} height (in pixels)
25403 * @cfg {Number} width (in pixels)
25408 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25411 stylesheets: false,
25416 // private properties
25417 validationEvent : false,
25419 initialized : false,
25422 onFocus : Roo.emptyFn,
25424 hideMode:'offsets',
25426 tbContainer : false,
25430 toolbarContainer :function() {
25431 return this.wrap.select('.x-html-editor-tb',true).first();
25435 * Protected method that will not generally be called directly. It
25436 * is called when the editor creates its toolbar. Override this method if you need to
25437 * add custom toolbar buttons.
25438 * @param {HtmlEditor} editor
25440 createToolbar : function(){
25441 Roo.log('renewing');
25442 Roo.log("create toolbars");
25444 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25445 this.toolbars[0].render(this.toolbarContainer());
25449 // if (!editor.toolbars || !editor.toolbars.length) {
25450 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25453 // for (var i =0 ; i < editor.toolbars.length;i++) {
25454 // editor.toolbars[i] = Roo.factory(
25455 // typeof(editor.toolbars[i]) == 'string' ?
25456 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25457 // Roo.bootstrap.HtmlEditor);
25458 // editor.toolbars[i].init(editor);
25464 onRender : function(ct, position)
25466 // Roo.log("Call onRender: " + this.xtype);
25468 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25470 this.wrap = this.inputEl().wrap({
25471 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25474 this.editorcore.onRender(ct, position);
25476 if (this.resizable) {
25477 this.resizeEl = new Roo.Resizable(this.wrap, {
25481 minHeight : this.height,
25482 height: this.height,
25483 handles : this.resizable,
25486 resize : function(r, w, h) {
25487 _t.onResize(w,h); // -something
25493 this.createToolbar(this);
25496 if(!this.width && this.resizable){
25497 this.setSize(this.wrap.getSize());
25499 if (this.resizeEl) {
25500 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25501 // should trigger onReize..
25507 onResize : function(w, h)
25509 Roo.log('resize: ' +w + ',' + h );
25510 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25514 if(this.inputEl() ){
25515 if(typeof w == 'number'){
25516 var aw = w - this.wrap.getFrameWidth('lr');
25517 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25520 if(typeof h == 'number'){
25521 var tbh = -11; // fixme it needs to tool bar size!
25522 for (var i =0; i < this.toolbars.length;i++) {
25523 // fixme - ask toolbars for heights?
25524 tbh += this.toolbars[i].el.getHeight();
25525 //if (this.toolbars[i].footer) {
25526 // tbh += this.toolbars[i].footer.el.getHeight();
25534 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25535 ah -= 5; // knock a few pixes off for look..
25536 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25540 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25541 this.editorcore.onResize(ew,eh);
25546 * Toggles the editor between standard and source edit mode.
25547 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25549 toggleSourceEdit : function(sourceEditMode)
25551 this.editorcore.toggleSourceEdit(sourceEditMode);
25553 if(this.editorcore.sourceEditMode){
25554 Roo.log('editor - showing textarea');
25557 // Roo.log(this.syncValue());
25559 this.inputEl().removeClass(['hide', 'x-hidden']);
25560 this.inputEl().dom.removeAttribute('tabIndex');
25561 this.inputEl().focus();
25563 Roo.log('editor - hiding textarea');
25565 // Roo.log(this.pushValue());
25568 this.inputEl().addClass(['hide', 'x-hidden']);
25569 this.inputEl().dom.setAttribute('tabIndex', -1);
25570 //this.deferFocus();
25573 if(this.resizable){
25574 this.setSize(this.wrap.getSize());
25577 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25580 // private (for BoxComponent)
25581 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25583 // private (for BoxComponent)
25584 getResizeEl : function(){
25588 // private (for BoxComponent)
25589 getPositionEl : function(){
25594 initEvents : function(){
25595 this.originalValue = this.getValue();
25599 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25602 // markInvalid : Roo.emptyFn,
25604 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25607 // clearInvalid : Roo.emptyFn,
25609 setValue : function(v){
25610 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25611 this.editorcore.pushValue();
25616 deferFocus : function(){
25617 this.focus.defer(10, this);
25621 focus : function(){
25622 this.editorcore.focus();
25628 onDestroy : function(){
25634 for (var i =0; i < this.toolbars.length;i++) {
25635 // fixme - ask toolbars for heights?
25636 this.toolbars[i].onDestroy();
25639 this.wrap.dom.innerHTML = '';
25640 this.wrap.remove();
25645 onFirstFocus : function(){
25646 //Roo.log("onFirstFocus");
25647 this.editorcore.onFirstFocus();
25648 for (var i =0; i < this.toolbars.length;i++) {
25649 this.toolbars[i].onFirstFocus();
25655 syncValue : function()
25657 this.editorcore.syncValue();
25660 pushValue : function()
25662 this.editorcore.pushValue();
25666 // hide stuff that is not compatible
25680 * @event specialkey
25684 * @cfg {String} fieldClass @hide
25687 * @cfg {String} focusClass @hide
25690 * @cfg {String} autoCreate @hide
25693 * @cfg {String} inputType @hide
25697 * @cfg {String} invalidText @hide
25700 * @cfg {String} msgFx @hide
25703 * @cfg {String} validateOnBlur @hide
25712 Roo.namespace('Roo.bootstrap.htmleditor');
25714 * @class Roo.bootstrap.HtmlEditorToolbar1
25720 new Roo.bootstrap.HtmlEditor({
25723 new Roo.bootstrap.HtmlEditorToolbar1({
25724 disable : { fonts: 1 , format: 1, ..., ... , ...],
25730 * @cfg {Object} disable List of elements to disable..
25731 * @cfg {Array} btns List of additional buttons.
25735 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25738 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25741 Roo.apply(this, config);
25743 // default disabled, based on 'good practice'..
25744 this.disable = this.disable || {};
25745 Roo.applyIf(this.disable, {
25748 specialElements : true
25750 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25752 this.editor = config.editor;
25753 this.editorcore = config.editor.editorcore;
25755 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25757 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25758 // dont call parent... till later.
25760 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25765 editorcore : false,
25770 "h1","h2","h3","h4","h5","h6",
25772 "abbr", "acronym", "address", "cite", "samp", "var",
25776 onRender : function(ct, position)
25778 // Roo.log("Call onRender: " + this.xtype);
25780 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25782 this.el.dom.style.marginBottom = '0';
25784 var editorcore = this.editorcore;
25785 var editor= this.editor;
25788 var btn = function(id,cmd , toggle, handler, html){
25790 var event = toggle ? 'toggle' : 'click';
25795 xns: Roo.bootstrap,
25799 enableToggle:toggle !== false,
25801 pressed : toggle ? false : null,
25804 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25805 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25811 // var cb_box = function...
25816 xns: Roo.bootstrap,
25821 xns: Roo.bootstrap,
25825 Roo.each(this.formats, function(f) {
25826 style.menu.items.push({
25828 xns: Roo.bootstrap,
25829 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25834 editorcore.insertTag(this.tagname);
25841 children.push(style);
25843 btn('bold',false,true);
25844 btn('italic',false,true);
25845 btn('align-left', 'justifyleft',true);
25846 btn('align-center', 'justifycenter',true);
25847 btn('align-right' , 'justifyright',true);
25848 btn('link', false, false, function(btn) {
25849 //Roo.log("create link?");
25850 var url = prompt(this.createLinkText, this.defaultLinkValue);
25851 if(url && url != 'http:/'+'/'){
25852 this.editorcore.relayCmd('createlink', url);
25855 btn('list','insertunorderedlist',true);
25856 btn('pencil', false,true, function(btn){
25858 this.toggleSourceEdit(btn.pressed);
25861 if (this.editor.btns.length > 0) {
25862 for (var i = 0; i<this.editor.btns.length; i++) {
25863 children.push(this.editor.btns[i]);
25871 xns: Roo.bootstrap,
25876 xns: Roo.bootstrap,
25881 cog.menu.items.push({
25883 xns: Roo.bootstrap,
25884 html : Clean styles,
25889 editorcore.insertTag(this.tagname);
25898 this.xtype = 'NavSimplebar';
25900 for(var i=0;i< children.length;i++) {
25902 this.buttons.add(this.addxtypeChild(children[i]));
25906 editor.on('editorevent', this.updateToolbar, this);
25908 onBtnClick : function(id)
25910 this.editorcore.relayCmd(id);
25911 this.editorcore.focus();
25915 * Protected method that will not generally be called directly. It triggers
25916 * a toolbar update by reading the markup state of the current selection in the editor.
25918 updateToolbar: function(){
25920 if(!this.editorcore.activated){
25921 this.editor.onFirstFocus(); // is this neeed?
25925 var btns = this.buttons;
25926 var doc = this.editorcore.doc;
25927 btns.get('bold').setActive(doc.queryCommandState('bold'));
25928 btns.get('italic').setActive(doc.queryCommandState('italic'));
25929 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25931 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25932 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25933 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25935 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25936 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25939 var ans = this.editorcore.getAllAncestors();
25940 if (this.formatCombo) {
25943 var store = this.formatCombo.store;
25944 this.formatCombo.setValue("");
25945 for (var i =0; i < ans.length;i++) {
25946 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25948 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25956 // hides menus... - so this cant be on a menu...
25957 Roo.bootstrap.MenuMgr.hideAll();
25959 Roo.bootstrap.MenuMgr.hideAll();
25960 //this.editorsyncValue();
25962 onFirstFocus: function() {
25963 this.buttons.each(function(item){
25967 toggleSourceEdit : function(sourceEditMode){
25970 if(sourceEditMode){
25971 Roo.log("disabling buttons");
25972 this.buttons.each( function(item){
25973 if(item.cmd != 'pencil'){
25979 Roo.log("enabling buttons");
25980 if(this.editorcore.initialized){
25981 this.buttons.each( function(item){
25987 Roo.log("calling toggole on editor");
25988 // tell the editor that it's been pressed..
25989 this.editor.toggleSourceEdit(sourceEditMode);
25999 * @class Roo.bootstrap.Table.AbstractSelectionModel
26000 * @extends Roo.util.Observable
26001 * Abstract base class for grid SelectionModels. It provides the interface that should be
26002 * implemented by descendant classes. This class should not be directly instantiated.
26005 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26006 this.locked = false;
26007 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26011 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26012 /** @ignore Called by the grid automatically. Do not call directly. */
26013 init : function(grid){
26019 * Locks the selections.
26022 this.locked = true;
26026 * Unlocks the selections.
26028 unlock : function(){
26029 this.locked = false;
26033 * Returns true if the selections are locked.
26034 * @return {Boolean}
26036 isLocked : function(){
26037 return this.locked;
26041 initEvents : function ()
26047 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26048 * @class Roo.bootstrap.Table.RowSelectionModel
26049 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26050 * It supports multiple selections and keyboard selection/navigation.
26052 * @param {Object} config
26055 Roo.bootstrap.Table.RowSelectionModel = function(config){
26056 Roo.apply(this, config);
26057 this.selections = new Roo.util.MixedCollection(false, function(o){
26062 this.lastActive = false;
26066 * @event selectionchange
26067 * Fires when the selection changes
26068 * @param {SelectionModel} this
26070 "selectionchange" : true,
26072 * @event afterselectionchange
26073 * Fires after the selection changes (eg. by key press or clicking)
26074 * @param {SelectionModel} this
26076 "afterselectionchange" : true,
26078 * @event beforerowselect
26079 * Fires when a row is selected being selected, return false to cancel.
26080 * @param {SelectionModel} this
26081 * @param {Number} rowIndex The selected index
26082 * @param {Boolean} keepExisting False if other selections will be cleared
26084 "beforerowselect" : true,
26087 * Fires when a row is selected.
26088 * @param {SelectionModel} this
26089 * @param {Number} rowIndex The selected index
26090 * @param {Roo.data.Record} r The record
26092 "rowselect" : true,
26094 * @event rowdeselect
26095 * Fires when a row is deselected.
26096 * @param {SelectionModel} this
26097 * @param {Number} rowIndex The selected index
26099 "rowdeselect" : true
26101 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26102 this.locked = false;
26105 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26107 * @cfg {Boolean} singleSelect
26108 * True to allow selection of only one row at a time (defaults to false)
26110 singleSelect : false,
26113 initEvents : function()
26116 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26117 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26118 //}else{ // allow click to work like normal
26119 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26121 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26122 this.grid.on("rowclick", this.handleMouseDown, this);
26124 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26125 "up" : function(e){
26127 this.selectPrevious(e.shiftKey);
26128 }else if(this.last !== false && this.lastActive !== false){
26129 var last = this.last;
26130 this.selectRange(this.last, this.lastActive-1);
26131 this.grid.getView().focusRow(this.lastActive);
26132 if(last !== false){
26136 this.selectFirstRow();
26138 this.fireEvent("afterselectionchange", this);
26140 "down" : function(e){
26142 this.selectNext(e.shiftKey);
26143 }else if(this.last !== false && this.lastActive !== false){
26144 var last = this.last;
26145 this.selectRange(this.last, this.lastActive+1);
26146 this.grid.getView().focusRow(this.lastActive);
26147 if(last !== false){
26151 this.selectFirstRow();
26153 this.fireEvent("afterselectionchange", this);
26157 this.grid.store.on('load', function(){
26158 this.selections.clear();
26161 var view = this.grid.view;
26162 view.on("refresh", this.onRefresh, this);
26163 view.on("rowupdated", this.onRowUpdated, this);
26164 view.on("rowremoved", this.onRemove, this);
26169 onRefresh : function()
26171 var ds = this.grid.store, i, v = this.grid.view;
26172 var s = this.selections;
26173 s.each(function(r){
26174 if((i = ds.indexOfId(r.id)) != -1){
26183 onRemove : function(v, index, r){
26184 this.selections.remove(r);
26188 onRowUpdated : function(v, index, r){
26189 if(this.isSelected(r)){
26190 v.onRowSelect(index);
26196 * @param {Array} records The records to select
26197 * @param {Boolean} keepExisting (optional) True to keep existing selections
26199 selectRecords : function(records, keepExisting)
26202 this.clearSelections();
26204 var ds = this.grid.store;
26205 for(var i = 0, len = records.length; i < len; i++){
26206 this.selectRow(ds.indexOf(records[i]), true);
26211 * Gets the number of selected rows.
26214 getCount : function(){
26215 return this.selections.length;
26219 * Selects the first row in the grid.
26221 selectFirstRow : function(){
26226 * Select the last row.
26227 * @param {Boolean} keepExisting (optional) True to keep existing selections
26229 selectLastRow : function(keepExisting){
26230 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26231 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26235 * Selects the row immediately following the last selected row.
26236 * @param {Boolean} keepExisting (optional) True to keep existing selections
26238 selectNext : function(keepExisting)
26240 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26241 this.selectRow(this.last+1, keepExisting);
26242 this.grid.getView().focusRow(this.last);
26247 * Selects the row that precedes the last selected row.
26248 * @param {Boolean} keepExisting (optional) True to keep existing selections
26250 selectPrevious : function(keepExisting){
26252 this.selectRow(this.last-1, keepExisting);
26253 this.grid.getView().focusRow(this.last);
26258 * Returns the selected records
26259 * @return {Array} Array of selected records
26261 getSelections : function(){
26262 return [].concat(this.selections.items);
26266 * Returns the first selected record.
26269 getSelected : function(){
26270 return this.selections.itemAt(0);
26275 * Clears all selections.
26277 clearSelections : function(fast)
26283 var ds = this.grid.store;
26284 var s = this.selections;
26285 s.each(function(r){
26286 this.deselectRow(ds.indexOfId(r.id));
26290 this.selections.clear();
26297 * Selects all rows.
26299 selectAll : function(){
26303 this.selections.clear();
26304 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26305 this.selectRow(i, true);
26310 * Returns True if there is a selection.
26311 * @return {Boolean}
26313 hasSelection : function(){
26314 return this.selections.length > 0;
26318 * Returns True if the specified row is selected.
26319 * @param {Number/Record} record The record or index of the record to check
26320 * @return {Boolean}
26322 isSelected : function(index){
26323 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26324 return (r && this.selections.key(r.id) ? true : false);
26328 * Returns True if the specified record id is selected.
26329 * @param {String} id The id of record to check
26330 * @return {Boolean}
26332 isIdSelected : function(id){
26333 return (this.selections.key(id) ? true : false);
26338 handleMouseDBClick : function(e, t){
26342 handleMouseDown : function(e, t)
26344 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26345 if(this.isLocked() || rowIndex < 0 ){
26348 if(e.shiftKey && this.last !== false){
26349 var last = this.last;
26350 this.selectRange(last, rowIndex, e.ctrlKey);
26351 this.last = last; // reset the last
26355 var isSelected = this.isSelected(rowIndex);
26356 //Roo.log("select row:" + rowIndex);
26358 this.deselectRow(rowIndex);
26360 this.selectRow(rowIndex, true);
26364 if(e.button !== 0 && isSelected){
26365 alert('rowIndex 2: ' + rowIndex);
26366 view.focusRow(rowIndex);
26367 }else if(e.ctrlKey && isSelected){
26368 this.deselectRow(rowIndex);
26369 }else if(!isSelected){
26370 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26371 view.focusRow(rowIndex);
26375 this.fireEvent("afterselectionchange", this);
26378 handleDragableRowClick : function(grid, rowIndex, e)
26380 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26381 this.selectRow(rowIndex, false);
26382 grid.view.focusRow(rowIndex);
26383 this.fireEvent("afterselectionchange", this);
26388 * Selects multiple rows.
26389 * @param {Array} rows Array of the indexes of the row to select
26390 * @param {Boolean} keepExisting (optional) True to keep existing selections
26392 selectRows : function(rows, keepExisting){
26394 this.clearSelections();
26396 for(var i = 0, len = rows.length; i < len; i++){
26397 this.selectRow(rows[i], true);
26402 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26403 * @param {Number} startRow The index of the first row in the range
26404 * @param {Number} endRow The index of the last row in the range
26405 * @param {Boolean} keepExisting (optional) True to retain existing selections
26407 selectRange : function(startRow, endRow, keepExisting){
26412 this.clearSelections();
26414 if(startRow <= endRow){
26415 for(var i = startRow; i <= endRow; i++){
26416 this.selectRow(i, true);
26419 for(var i = startRow; i >= endRow; i--){
26420 this.selectRow(i, true);
26426 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26427 * @param {Number} startRow The index of the first row in the range
26428 * @param {Number} endRow The index of the last row in the range
26430 deselectRange : function(startRow, endRow, preventViewNotify){
26434 for(var i = startRow; i <= endRow; i++){
26435 this.deselectRow(i, preventViewNotify);
26441 * @param {Number} row The index of the row to select
26442 * @param {Boolean} keepExisting (optional) True to keep existing selections
26444 selectRow : function(index, keepExisting, preventViewNotify)
26446 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26449 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26450 if(!keepExisting || this.singleSelect){
26451 this.clearSelections();
26454 var r = this.grid.store.getAt(index);
26455 //console.log('selectRow - record id :' + r.id);
26457 this.selections.add(r);
26458 this.last = this.lastActive = index;
26459 if(!preventViewNotify){
26460 var proxy = new Roo.Element(
26461 this.grid.getRowDom(index)
26463 proxy.addClass('bg-info info');
26465 this.fireEvent("rowselect", this, index, r);
26466 this.fireEvent("selectionchange", this);
26472 * @param {Number} row The index of the row to deselect
26474 deselectRow : function(index, preventViewNotify)
26479 if(this.last == index){
26482 if(this.lastActive == index){
26483 this.lastActive = false;
26486 var r = this.grid.store.getAt(index);
26491 this.selections.remove(r);
26492 //.console.log('deselectRow - record id :' + r.id);
26493 if(!preventViewNotify){
26495 var proxy = new Roo.Element(
26496 this.grid.getRowDom(index)
26498 proxy.removeClass('bg-info info');
26500 this.fireEvent("rowdeselect", this, index);
26501 this.fireEvent("selectionchange", this);
26505 restoreLast : function(){
26507 this.last = this._last;
26512 acceptsNav : function(row, col, cm){
26513 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26517 onEditorKey : function(field, e){
26518 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26523 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26525 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26527 }else if(k == e.ENTER && !e.ctrlKey){
26531 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26533 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26535 }else if(k == e.ESC){
26539 g.startEditing(newCell[0], newCell[1]);
26545 * Ext JS Library 1.1.1
26546 * Copyright(c) 2006-2007, Ext JS, LLC.
26548 * Originally Released Under LGPL - original licence link has changed is not relivant.
26551 * <script type="text/javascript">
26555 * @class Roo.bootstrap.PagingToolbar
26556 * @extends Roo.bootstrap.NavSimplebar
26557 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26559 * Create a new PagingToolbar
26560 * @param {Object} config The config object
26561 * @param {Roo.data.Store} store
26563 Roo.bootstrap.PagingToolbar = function(config)
26565 // old args format still supported... - xtype is prefered..
26566 // created from xtype...
26568 this.ds = config.dataSource;
26570 if (config.store && !this.ds) {
26571 this.store= Roo.factory(config.store, Roo.data);
26572 this.ds = this.store;
26573 this.ds.xmodule = this.xmodule || false;
26576 this.toolbarItems = [];
26577 if (config.items) {
26578 this.toolbarItems = config.items;
26581 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26586 this.bind(this.ds);
26589 if (Roo.bootstrap.version == 4) {
26590 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26592 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26597 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26599 * @cfg {Roo.data.Store} dataSource
26600 * The underlying data store providing the paged data
26603 * @cfg {String/HTMLElement/Element} container
26604 * container The id or element that will contain the toolbar
26607 * @cfg {Boolean} displayInfo
26608 * True to display the displayMsg (defaults to false)
26611 * @cfg {Number} pageSize
26612 * The number of records to display per page (defaults to 20)
26616 * @cfg {String} displayMsg
26617 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26619 displayMsg : 'Displaying {0} - {1} of {2}',
26621 * @cfg {String} emptyMsg
26622 * The message to display when no records are found (defaults to "No data to display")
26624 emptyMsg : 'No data to display',
26626 * Customizable piece of the default paging text (defaults to "Page")
26629 beforePageText : "Page",
26631 * Customizable piece of the default paging text (defaults to "of %0")
26634 afterPageText : "of {0}",
26636 * Customizable piece of the default paging text (defaults to "First Page")
26639 firstText : "First Page",
26641 * Customizable piece of the default paging text (defaults to "Previous Page")
26644 prevText : "Previous Page",
26646 * Customizable piece of the default paging text (defaults to "Next Page")
26649 nextText : "Next Page",
26651 * Customizable piece of the default paging text (defaults to "Last Page")
26654 lastText : "Last Page",
26656 * Customizable piece of the default paging text (defaults to "Refresh")
26659 refreshText : "Refresh",
26663 onRender : function(ct, position)
26665 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26666 this.navgroup.parentId = this.id;
26667 this.navgroup.onRender(this.el, null);
26668 // add the buttons to the navgroup
26670 if(this.displayInfo){
26671 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26672 this.displayEl = this.el.select('.x-paging-info', true).first();
26673 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26674 // this.displayEl = navel.el.select('span',true).first();
26680 Roo.each(_this.buttons, function(e){ // this might need to use render????
26681 Roo.factory(e).render(_this.el);
26685 Roo.each(_this.toolbarItems, function(e) {
26686 _this.navgroup.addItem(e);
26690 this.first = this.navgroup.addItem({
26691 tooltip: this.firstText,
26692 cls: "prev btn-outline-secondary",
26693 html : ' <i class="fa fa-step-backward"></i>',
26695 preventDefault: true,
26696 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26699 this.prev = this.navgroup.addItem({
26700 tooltip: this.prevText,
26701 cls: "prev btn-outline-secondary",
26702 html : ' <i class="fa fa-backward"></i>',
26704 preventDefault: true,
26705 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26707 //this.addSeparator();
26710 var field = this.navgroup.addItem( {
26712 cls : 'x-paging-position btn-outline-secondary',
26714 html : this.beforePageText +
26715 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26716 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26719 this.field = field.el.select('input', true).first();
26720 this.field.on("keydown", this.onPagingKeydown, this);
26721 this.field.on("focus", function(){this.dom.select();});
26724 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26725 //this.field.setHeight(18);
26726 //this.addSeparator();
26727 this.next = this.navgroup.addItem({
26728 tooltip: this.nextText,
26729 cls: "next btn-outline-secondary",
26730 html : ' <i class="fa fa-forward"></i>',
26732 preventDefault: true,
26733 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26735 this.last = this.navgroup.addItem({
26736 tooltip: this.lastText,
26737 html : ' <i class="fa fa-step-forward"></i>',
26738 cls: "next btn-outline-secondary",
26740 preventDefault: true,
26741 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26743 //this.addSeparator();
26744 this.loading = this.navgroup.addItem({
26745 tooltip: this.refreshText,
26746 cls: "btn-outline-secondary",
26747 html : ' <i class="fa fa-refresh"></i>',
26748 preventDefault: true,
26749 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26755 updateInfo : function(){
26756 if(this.displayEl){
26757 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26758 var msg = count == 0 ?
26762 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26764 this.displayEl.update(msg);
26769 onLoad : function(ds, r, o)
26771 this.cursor = o.params.start ? o.params.start : 0;
26773 var d = this.getPageData(),
26778 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26779 this.field.dom.value = ap;
26780 this.first.setDisabled(ap == 1);
26781 this.prev.setDisabled(ap == 1);
26782 this.next.setDisabled(ap == ps);
26783 this.last.setDisabled(ap == ps);
26784 this.loading.enable();
26789 getPageData : function(){
26790 var total = this.ds.getTotalCount();
26793 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26794 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26799 onLoadError : function(){
26800 this.loading.enable();
26804 onPagingKeydown : function(e){
26805 var k = e.getKey();
26806 var d = this.getPageData();
26808 var v = this.field.dom.value, pageNum;
26809 if(!v || isNaN(pageNum = parseInt(v, 10))){
26810 this.field.dom.value = d.activePage;
26813 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26814 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26817 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))
26819 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26820 this.field.dom.value = pageNum;
26821 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26824 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26826 var v = this.field.dom.value, pageNum;
26827 var increment = (e.shiftKey) ? 10 : 1;
26828 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26831 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26832 this.field.dom.value = d.activePage;
26835 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26837 this.field.dom.value = parseInt(v, 10) + increment;
26838 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26839 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26846 beforeLoad : function(){
26848 this.loading.disable();
26853 onClick : function(which){
26862 ds.load({params:{start: 0, limit: this.pageSize}});
26865 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26868 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26871 var total = ds.getTotalCount();
26872 var extra = total % this.pageSize;
26873 var lastStart = extra ? (total - extra) : total-this.pageSize;
26874 ds.load({params:{start: lastStart, limit: this.pageSize}});
26877 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26883 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26884 * @param {Roo.data.Store} store The data store to unbind
26886 unbind : function(ds){
26887 ds.un("beforeload", this.beforeLoad, this);
26888 ds.un("load", this.onLoad, this);
26889 ds.un("loadexception", this.onLoadError, this);
26890 ds.un("remove", this.updateInfo, this);
26891 ds.un("add", this.updateInfo, this);
26892 this.ds = undefined;
26896 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26897 * @param {Roo.data.Store} store The data store to bind
26899 bind : function(ds){
26900 ds.on("beforeload", this.beforeLoad, this);
26901 ds.on("load", this.onLoad, this);
26902 ds.on("loadexception", this.onLoadError, this);
26903 ds.on("remove", this.updateInfo, this);
26904 ds.on("add", this.updateInfo, this);
26915 * @class Roo.bootstrap.MessageBar
26916 * @extends Roo.bootstrap.Component
26917 * Bootstrap MessageBar class
26918 * @cfg {String} html contents of the MessageBar
26919 * @cfg {String} weight (info | success | warning | danger) default info
26920 * @cfg {String} beforeClass insert the bar before the given class
26921 * @cfg {Boolean} closable (true | false) default false
26922 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26925 * Create a new Element
26926 * @param {Object} config The config object
26929 Roo.bootstrap.MessageBar = function(config){
26930 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26933 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26939 beforeClass: 'bootstrap-sticky-wrap',
26941 getAutoCreate : function(){
26945 cls: 'alert alert-dismissable alert-' + this.weight,
26950 html: this.html || ''
26956 cfg.cls += ' alert-messages-fixed';
26970 onRender : function(ct, position)
26972 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26975 var cfg = Roo.apply({}, this.getAutoCreate());
26979 cfg.cls += ' ' + this.cls;
26982 cfg.style = this.style;
26984 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26986 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26989 this.el.select('>button.close').on('click', this.hide, this);
26995 if (!this.rendered) {
27001 this.fireEvent('show', this);
27007 if (!this.rendered) {
27013 this.fireEvent('hide', this);
27016 update : function()
27018 // var e = this.el.dom.firstChild;
27020 // if(this.closable){
27021 // e = e.nextSibling;
27024 // e.data = this.html || '';
27026 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27042 * @class Roo.bootstrap.Graph
27043 * @extends Roo.bootstrap.Component
27044 * Bootstrap Graph class
27048 @cfg {String} graphtype bar | vbar | pie
27049 @cfg {number} g_x coodinator | centre x (pie)
27050 @cfg {number} g_y coodinator | centre y (pie)
27051 @cfg {number} g_r radius (pie)
27052 @cfg {number} g_height height of the chart (respected by all elements in the set)
27053 @cfg {number} g_width width of the chart (respected by all elements in the set)
27054 @cfg {Object} title The title of the chart
27057 -opts (object) options for the chart
27059 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27060 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27062 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.
27063 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27065 o stretch (boolean)
27067 -opts (object) options for the pie
27070 o startAngle (number)
27071 o endAngle (number)
27075 * Create a new Input
27076 * @param {Object} config The config object
27079 Roo.bootstrap.Graph = function(config){
27080 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27086 * The img click event for the img.
27087 * @param {Roo.EventObject} e
27093 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27104 //g_colors: this.colors,
27111 getAutoCreate : function(){
27122 onRender : function(ct,position){
27125 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27127 if (typeof(Raphael) == 'undefined') {
27128 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27132 this.raphael = Raphael(this.el.dom);
27134 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27135 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27136 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27137 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27139 r.text(160, 10, "Single Series Chart").attr(txtattr);
27140 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27141 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27142 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27144 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27145 r.barchart(330, 10, 300, 220, data1);
27146 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27147 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27150 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27151 // r.barchart(30, 30, 560, 250, xdata, {
27152 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27153 // axis : "0 0 1 1",
27154 // axisxlabels : xdata
27155 // //yvalues : cols,
27158 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27160 // this.load(null,xdata,{
27161 // axis : "0 0 1 1",
27162 // axisxlabels : xdata
27167 load : function(graphtype,xdata,opts)
27169 this.raphael.clear();
27171 graphtype = this.graphtype;
27176 var r = this.raphael,
27177 fin = function () {
27178 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27180 fout = function () {
27181 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27183 pfin = function() {
27184 this.sector.stop();
27185 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27188 this.label[0].stop();
27189 this.label[0].attr({ r: 7.5 });
27190 this.label[1].attr({ "font-weight": 800 });
27193 pfout = function() {
27194 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27197 this.label[0].animate({ r: 5 }, 500, "bounce");
27198 this.label[1].attr({ "font-weight": 400 });
27204 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27207 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27210 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27211 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27213 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27220 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27225 setTitle: function(o)
27230 initEvents: function() {
27233 this.el.on('click', this.onClick, this);
27237 onClick : function(e)
27239 Roo.log('img onclick');
27240 this.fireEvent('click', this, e);
27252 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27255 * @class Roo.bootstrap.dash.NumberBox
27256 * @extends Roo.bootstrap.Component
27257 * Bootstrap NumberBox class
27258 * @cfg {String} headline Box headline
27259 * @cfg {String} content Box content
27260 * @cfg {String} icon Box icon
27261 * @cfg {String} footer Footer text
27262 * @cfg {String} fhref Footer href
27265 * Create a new NumberBox
27266 * @param {Object} config The config object
27270 Roo.bootstrap.dash.NumberBox = function(config){
27271 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27275 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27284 getAutoCreate : function(){
27288 cls : 'small-box ',
27296 cls : 'roo-headline',
27297 html : this.headline
27301 cls : 'roo-content',
27302 html : this.content
27316 cls : 'ion ' + this.icon
27325 cls : 'small-box-footer',
27326 href : this.fhref || '#',
27330 cfg.cn.push(footer);
27337 onRender : function(ct,position){
27338 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27345 setHeadline: function (value)
27347 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27350 setFooter: function (value, href)
27352 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27355 this.el.select('a.small-box-footer',true).first().attr('href', href);
27360 setContent: function (value)
27362 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27365 initEvents: function()
27379 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27382 * @class Roo.bootstrap.dash.TabBox
27383 * @extends Roo.bootstrap.Component
27384 * Bootstrap TabBox class
27385 * @cfg {String} title Title of the TabBox
27386 * @cfg {String} icon Icon of the TabBox
27387 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27388 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27391 * Create a new TabBox
27392 * @param {Object} config The config object
27396 Roo.bootstrap.dash.TabBox = function(config){
27397 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27402 * When a pane is added
27403 * @param {Roo.bootstrap.dash.TabPane} pane
27407 * @event activatepane
27408 * When a pane is activated
27409 * @param {Roo.bootstrap.dash.TabPane} pane
27411 "activatepane" : true
27419 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27424 tabScrollable : false,
27426 getChildContainer : function()
27428 return this.el.select('.tab-content', true).first();
27431 getAutoCreate : function(){
27435 cls: 'pull-left header',
27443 cls: 'fa ' + this.icon
27449 cls: 'nav nav-tabs pull-right',
27455 if(this.tabScrollable){
27462 cls: 'nav nav-tabs pull-right',
27473 cls: 'nav-tabs-custom',
27478 cls: 'tab-content no-padding',
27486 initEvents : function()
27488 //Roo.log('add add pane handler');
27489 this.on('addpane', this.onAddPane, this);
27492 * Updates the box title
27493 * @param {String} html to set the title to.
27495 setTitle : function(value)
27497 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27499 onAddPane : function(pane)
27501 this.panes.push(pane);
27502 //Roo.log('addpane');
27504 // tabs are rendere left to right..
27505 if(!this.showtabs){
27509 var ctr = this.el.select('.nav-tabs', true).first();
27512 var existing = ctr.select('.nav-tab',true);
27513 var qty = existing.getCount();;
27516 var tab = ctr.createChild({
27518 cls : 'nav-tab' + (qty ? '' : ' active'),
27526 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27529 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27531 pane.el.addClass('active');
27536 onTabClick : function(ev,un,ob,pane)
27538 //Roo.log('tab - prev default');
27539 ev.preventDefault();
27542 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27543 pane.tab.addClass('active');
27544 //Roo.log(pane.title);
27545 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27546 // technically we should have a deactivate event.. but maybe add later.
27547 // and it should not de-activate the selected tab...
27548 this.fireEvent('activatepane', pane);
27549 pane.el.addClass('active');
27550 pane.fireEvent('activate');
27555 getActivePane : function()
27558 Roo.each(this.panes, function(p) {
27559 if(p.el.hasClass('active')){
27580 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27582 * @class Roo.bootstrap.TabPane
27583 * @extends Roo.bootstrap.Component
27584 * Bootstrap TabPane class
27585 * @cfg {Boolean} active (false | true) Default false
27586 * @cfg {String} title title of panel
27590 * Create a new TabPane
27591 * @param {Object} config The config object
27594 Roo.bootstrap.dash.TabPane = function(config){
27595 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27601 * When a pane is activated
27602 * @param {Roo.bootstrap.dash.TabPane} pane
27609 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27614 // the tabBox that this is attached to.
27617 getAutoCreate : function()
27625 cfg.cls += ' active';
27630 initEvents : function()
27632 //Roo.log('trigger add pane handler');
27633 this.parent().fireEvent('addpane', this)
27637 * Updates the tab title
27638 * @param {String} html to set the title to.
27640 setTitle: function(str)
27646 this.tab.select('a', true).first().dom.innerHTML = str;
27663 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27666 * @class Roo.bootstrap.menu.Menu
27667 * @extends Roo.bootstrap.Component
27668 * Bootstrap Menu class - container for Menu
27669 * @cfg {String} html Text of the menu
27670 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27671 * @cfg {String} icon Font awesome icon
27672 * @cfg {String} pos Menu align to (top | bottom) default bottom
27676 * Create a new Menu
27677 * @param {Object} config The config object
27681 Roo.bootstrap.menu.Menu = function(config){
27682 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27686 * @event beforeshow
27687 * Fires before this menu is displayed
27688 * @param {Roo.bootstrap.menu.Menu} this
27692 * @event beforehide
27693 * Fires before this menu is hidden
27694 * @param {Roo.bootstrap.menu.Menu} this
27699 * Fires after this menu is displayed
27700 * @param {Roo.bootstrap.menu.Menu} this
27705 * Fires after this menu is hidden
27706 * @param {Roo.bootstrap.menu.Menu} this
27711 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27712 * @param {Roo.bootstrap.menu.Menu} this
27713 * @param {Roo.EventObject} e
27720 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27724 weight : 'default',
27729 getChildContainer : function() {
27730 if(this.isSubMenu){
27734 return this.el.select('ul.dropdown-menu', true).first();
27737 getAutoCreate : function()
27742 cls : 'roo-menu-text',
27750 cls : 'fa ' + this.icon
27761 cls : 'dropdown-button btn btn-' + this.weight,
27766 cls : 'dropdown-toggle btn btn-' + this.weight,
27776 cls : 'dropdown-menu'
27782 if(this.pos == 'top'){
27783 cfg.cls += ' dropup';
27786 if(this.isSubMenu){
27789 cls : 'dropdown-menu'
27796 onRender : function(ct, position)
27798 this.isSubMenu = ct.hasClass('dropdown-submenu');
27800 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27803 initEvents : function()
27805 if(this.isSubMenu){
27809 this.hidden = true;
27811 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27812 this.triggerEl.on('click', this.onTriggerPress, this);
27814 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27815 this.buttonEl.on('click', this.onClick, this);
27821 if(this.isSubMenu){
27825 return this.el.select('ul.dropdown-menu', true).first();
27828 onClick : function(e)
27830 this.fireEvent("click", this, e);
27833 onTriggerPress : function(e)
27835 if (this.isVisible()) {
27842 isVisible : function(){
27843 return !this.hidden;
27848 this.fireEvent("beforeshow", this);
27850 this.hidden = false;
27851 this.el.addClass('open');
27853 Roo.get(document).on("mouseup", this.onMouseUp, this);
27855 this.fireEvent("show", this);
27862 this.fireEvent("beforehide", this);
27864 this.hidden = true;
27865 this.el.removeClass('open');
27867 Roo.get(document).un("mouseup", this.onMouseUp);
27869 this.fireEvent("hide", this);
27872 onMouseUp : function()
27886 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27889 * @class Roo.bootstrap.menu.Item
27890 * @extends Roo.bootstrap.Component
27891 * Bootstrap MenuItem class
27892 * @cfg {Boolean} submenu (true | false) default false
27893 * @cfg {String} html text of the item
27894 * @cfg {String} href the link
27895 * @cfg {Boolean} disable (true | false) default false
27896 * @cfg {Boolean} preventDefault (true | false) default true
27897 * @cfg {String} icon Font awesome icon
27898 * @cfg {String} pos Submenu align to (left | right) default right
27902 * Create a new Item
27903 * @param {Object} config The config object
27907 Roo.bootstrap.menu.Item = function(config){
27908 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27912 * Fires when the mouse is hovering over this menu
27913 * @param {Roo.bootstrap.menu.Item} this
27914 * @param {Roo.EventObject} e
27919 * Fires when the mouse exits this menu
27920 * @param {Roo.bootstrap.menu.Item} this
27921 * @param {Roo.EventObject} e
27927 * The raw click event for the entire grid.
27928 * @param {Roo.EventObject} e
27934 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27939 preventDefault: true,
27944 getAutoCreate : function()
27949 cls : 'roo-menu-item-text',
27957 cls : 'fa ' + this.icon
27966 href : this.href || '#',
27973 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27977 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27979 if(this.pos == 'left'){
27980 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27987 initEvents : function()
27989 this.el.on('mouseover', this.onMouseOver, this);
27990 this.el.on('mouseout', this.onMouseOut, this);
27992 this.el.select('a', true).first().on('click', this.onClick, this);
27996 onClick : function(e)
27998 if(this.preventDefault){
27999 e.preventDefault();
28002 this.fireEvent("click", this, e);
28005 onMouseOver : function(e)
28007 if(this.submenu && this.pos == 'left'){
28008 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28011 this.fireEvent("mouseover", this, e);
28014 onMouseOut : function(e)
28016 this.fireEvent("mouseout", this, e);
28028 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28031 * @class Roo.bootstrap.menu.Separator
28032 * @extends Roo.bootstrap.Component
28033 * Bootstrap Separator class
28036 * Create a new Separator
28037 * @param {Object} config The config object
28041 Roo.bootstrap.menu.Separator = function(config){
28042 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28045 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28047 getAutoCreate : function(){
28068 * @class Roo.bootstrap.Tooltip
28069 * Bootstrap Tooltip class
28070 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28071 * to determine which dom element triggers the tooltip.
28073 * It needs to add support for additional attributes like tooltip-position
28076 * Create a new Toolti
28077 * @param {Object} config The config object
28080 Roo.bootstrap.Tooltip = function(config){
28081 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28083 this.alignment = Roo.bootstrap.Tooltip.alignment;
28085 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28086 this.alignment = config.alignment;
28091 Roo.apply(Roo.bootstrap.Tooltip, {
28093 * @function init initialize tooltip monitoring.
28097 currentTip : false,
28098 currentRegion : false,
28104 Roo.get(document).on('mouseover', this.enter ,this);
28105 Roo.get(document).on('mouseout', this.leave, this);
28108 this.currentTip = new Roo.bootstrap.Tooltip();
28111 enter : function(ev)
28113 var dom = ev.getTarget();
28115 //Roo.log(['enter',dom]);
28116 var el = Roo.fly(dom);
28117 if (this.currentEl) {
28119 //Roo.log(this.currentEl);
28120 //Roo.log(this.currentEl.contains(dom));
28121 if (this.currentEl == el) {
28124 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28130 if (this.currentTip.el) {
28131 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28135 if(!el || el.dom == document){
28141 // you can not look for children, as if el is the body.. then everythign is the child..
28142 if (!el.attr('tooltip')) { //
28143 if (!el.select("[tooltip]").elements.length) {
28146 // is the mouse over this child...?
28147 bindEl = el.select("[tooltip]").first();
28148 var xy = ev.getXY();
28149 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28150 //Roo.log("not in region.");
28153 //Roo.log("child element over..");
28156 this.currentEl = bindEl;
28157 this.currentTip.bind(bindEl);
28158 this.currentRegion = Roo.lib.Region.getRegion(dom);
28159 this.currentTip.enter();
28162 leave : function(ev)
28164 var dom = ev.getTarget();
28165 //Roo.log(['leave',dom]);
28166 if (!this.currentEl) {
28171 if (dom != this.currentEl.dom) {
28174 var xy = ev.getXY();
28175 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28178 // only activate leave if mouse cursor is outside... bounding box..
28183 if (this.currentTip) {
28184 this.currentTip.leave();
28186 //Roo.log('clear currentEl');
28187 this.currentEl = false;
28192 'left' : ['r-l', [-2,0], 'right'],
28193 'right' : ['l-r', [2,0], 'left'],
28194 'bottom' : ['t-b', [0,2], 'top'],
28195 'top' : [ 'b-t', [0,-2], 'bottom']
28201 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28206 delay : null, // can be { show : 300 , hide: 500}
28210 hoverState : null, //???
28212 placement : 'bottom',
28216 getAutoCreate : function(){
28223 cls : 'tooltip-arrow'
28226 cls : 'tooltip-inner'
28233 bind : function(el)
28239 enter : function () {
28241 if (this.timeout != null) {
28242 clearTimeout(this.timeout);
28245 this.hoverState = 'in';
28246 //Roo.log("enter - show");
28247 if (!this.delay || !this.delay.show) {
28252 this.timeout = setTimeout(function () {
28253 if (_t.hoverState == 'in') {
28256 }, this.delay.show);
28260 clearTimeout(this.timeout);
28262 this.hoverState = 'out';
28263 if (!this.delay || !this.delay.hide) {
28269 this.timeout = setTimeout(function () {
28270 //Roo.log("leave - timeout");
28272 if (_t.hoverState == 'out') {
28274 Roo.bootstrap.Tooltip.currentEl = false;
28279 show : function (msg)
28282 this.render(document.body);
28285 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28287 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28289 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28291 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28293 var placement = typeof this.placement == 'function' ?
28294 this.placement.call(this, this.el, on_el) :
28297 var autoToken = /\s?auto?\s?/i;
28298 var autoPlace = autoToken.test(placement);
28300 placement = placement.replace(autoToken, '') || 'top';
28304 //this.el.setXY([0,0]);
28306 //this.el.dom.style.display='block';
28308 //this.el.appendTo(on_el);
28310 var p = this.getPosition();
28311 var box = this.el.getBox();
28317 var align = this.alignment[placement];
28319 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28321 if(placement == 'top' || placement == 'bottom'){
28323 placement = 'right';
28326 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28327 placement = 'left';
28330 var scroll = Roo.select('body', true).first().getScroll();
28332 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28336 align = this.alignment[placement];
28339 this.el.alignTo(this.bindEl, align[0],align[1]);
28340 //var arrow = this.el.select('.arrow',true).first();
28341 //arrow.set(align[2],
28343 this.el.addClass(placement);
28345 this.el.addClass('in fade');
28347 this.hoverState = null;
28349 if (this.el.hasClass('fade')) {
28360 //this.el.setXY([0,0]);
28361 this.el.removeClass('in');
28377 * @class Roo.bootstrap.LocationPicker
28378 * @extends Roo.bootstrap.Component
28379 * Bootstrap LocationPicker class
28380 * @cfg {Number} latitude Position when init default 0
28381 * @cfg {Number} longitude Position when init default 0
28382 * @cfg {Number} zoom default 15
28383 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28384 * @cfg {Boolean} mapTypeControl default false
28385 * @cfg {Boolean} disableDoubleClickZoom default false
28386 * @cfg {Boolean} scrollwheel default true
28387 * @cfg {Boolean} streetViewControl default false
28388 * @cfg {Number} radius default 0
28389 * @cfg {String} locationName
28390 * @cfg {Boolean} draggable default true
28391 * @cfg {Boolean} enableAutocomplete default false
28392 * @cfg {Boolean} enableReverseGeocode default true
28393 * @cfg {String} markerTitle
28396 * Create a new LocationPicker
28397 * @param {Object} config The config object
28401 Roo.bootstrap.LocationPicker = function(config){
28403 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28408 * Fires when the picker initialized.
28409 * @param {Roo.bootstrap.LocationPicker} this
28410 * @param {Google Location} location
28414 * @event positionchanged
28415 * Fires when the picker position changed.
28416 * @param {Roo.bootstrap.LocationPicker} this
28417 * @param {Google Location} location
28419 positionchanged : true,
28422 * Fires when the map resize.
28423 * @param {Roo.bootstrap.LocationPicker} this
28428 * Fires when the map show.
28429 * @param {Roo.bootstrap.LocationPicker} this
28434 * Fires when the map hide.
28435 * @param {Roo.bootstrap.LocationPicker} this
28440 * Fires when click the map.
28441 * @param {Roo.bootstrap.LocationPicker} this
28442 * @param {Map event} e
28446 * @event mapRightClick
28447 * Fires when right click the map.
28448 * @param {Roo.bootstrap.LocationPicker} this
28449 * @param {Map event} e
28451 mapRightClick : true,
28453 * @event markerClick
28454 * Fires when click the marker.
28455 * @param {Roo.bootstrap.LocationPicker} this
28456 * @param {Map event} e
28458 markerClick : true,
28460 * @event markerRightClick
28461 * Fires when right click the marker.
28462 * @param {Roo.bootstrap.LocationPicker} this
28463 * @param {Map event} e
28465 markerRightClick : true,
28467 * @event OverlayViewDraw
28468 * Fires when OverlayView Draw
28469 * @param {Roo.bootstrap.LocationPicker} this
28471 OverlayViewDraw : true,
28473 * @event OverlayViewOnAdd
28474 * Fires when OverlayView Draw
28475 * @param {Roo.bootstrap.LocationPicker} this
28477 OverlayViewOnAdd : true,
28479 * @event OverlayViewOnRemove
28480 * Fires when OverlayView Draw
28481 * @param {Roo.bootstrap.LocationPicker} this
28483 OverlayViewOnRemove : true,
28485 * @event OverlayViewShow
28486 * Fires when OverlayView Draw
28487 * @param {Roo.bootstrap.LocationPicker} this
28488 * @param {Pixel} cpx
28490 OverlayViewShow : true,
28492 * @event OverlayViewHide
28493 * Fires when OverlayView Draw
28494 * @param {Roo.bootstrap.LocationPicker} this
28496 OverlayViewHide : true,
28498 * @event loadexception
28499 * Fires when load google lib failed.
28500 * @param {Roo.bootstrap.LocationPicker} this
28502 loadexception : true
28507 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28509 gMapContext: false,
28515 mapTypeControl: false,
28516 disableDoubleClickZoom: false,
28518 streetViewControl: false,
28522 enableAutocomplete: false,
28523 enableReverseGeocode: true,
28526 getAutoCreate: function()
28531 cls: 'roo-location-picker'
28537 initEvents: function(ct, position)
28539 if(!this.el.getWidth() || this.isApplied()){
28543 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28548 initial: function()
28550 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28551 this.fireEvent('loadexception', this);
28555 if(!this.mapTypeId){
28556 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28559 this.gMapContext = this.GMapContext();
28561 this.initOverlayView();
28563 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28567 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28568 _this.setPosition(_this.gMapContext.marker.position);
28571 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28572 _this.fireEvent('mapClick', this, event);
28576 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28577 _this.fireEvent('mapRightClick', this, event);
28581 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28582 _this.fireEvent('markerClick', this, event);
28586 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28587 _this.fireEvent('markerRightClick', this, event);
28591 this.setPosition(this.gMapContext.location);
28593 this.fireEvent('initial', this, this.gMapContext.location);
28596 initOverlayView: function()
28600 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28604 _this.fireEvent('OverlayViewDraw', _this);
28609 _this.fireEvent('OverlayViewOnAdd', _this);
28612 onRemove: function()
28614 _this.fireEvent('OverlayViewOnRemove', _this);
28617 show: function(cpx)
28619 _this.fireEvent('OverlayViewShow', _this, cpx);
28624 _this.fireEvent('OverlayViewHide', _this);
28630 fromLatLngToContainerPixel: function(event)
28632 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28635 isApplied: function()
28637 return this.getGmapContext() == false ? false : true;
28640 getGmapContext: function()
28642 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28645 GMapContext: function()
28647 var position = new google.maps.LatLng(this.latitude, this.longitude);
28649 var _map = new google.maps.Map(this.el.dom, {
28652 mapTypeId: this.mapTypeId,
28653 mapTypeControl: this.mapTypeControl,
28654 disableDoubleClickZoom: this.disableDoubleClickZoom,
28655 scrollwheel: this.scrollwheel,
28656 streetViewControl: this.streetViewControl,
28657 locationName: this.locationName,
28658 draggable: this.draggable,
28659 enableAutocomplete: this.enableAutocomplete,
28660 enableReverseGeocode: this.enableReverseGeocode
28663 var _marker = new google.maps.Marker({
28664 position: position,
28666 title: this.markerTitle,
28667 draggable: this.draggable
28674 location: position,
28675 radius: this.radius,
28676 locationName: this.locationName,
28677 addressComponents: {
28678 formatted_address: null,
28679 addressLine1: null,
28680 addressLine2: null,
28682 streetNumber: null,
28686 stateOrProvince: null
28689 domContainer: this.el.dom,
28690 geodecoder: new google.maps.Geocoder()
28694 drawCircle: function(center, radius, options)
28696 if (this.gMapContext.circle != null) {
28697 this.gMapContext.circle.setMap(null);
28701 options = Roo.apply({}, options, {
28702 strokeColor: "#0000FF",
28703 strokeOpacity: .35,
28705 fillColor: "#0000FF",
28709 options.map = this.gMapContext.map;
28710 options.radius = radius;
28711 options.center = center;
28712 this.gMapContext.circle = new google.maps.Circle(options);
28713 return this.gMapContext.circle;
28719 setPosition: function(location)
28721 this.gMapContext.location = location;
28722 this.gMapContext.marker.setPosition(location);
28723 this.gMapContext.map.panTo(location);
28724 this.drawCircle(location, this.gMapContext.radius, {});
28728 if (this.gMapContext.settings.enableReverseGeocode) {
28729 this.gMapContext.geodecoder.geocode({
28730 latLng: this.gMapContext.location
28731 }, function(results, status) {
28733 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28734 _this.gMapContext.locationName = results[0].formatted_address;
28735 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28737 _this.fireEvent('positionchanged', this, location);
28744 this.fireEvent('positionchanged', this, location);
28749 google.maps.event.trigger(this.gMapContext.map, "resize");
28751 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28753 this.fireEvent('resize', this);
28756 setPositionByLatLng: function(latitude, longitude)
28758 this.setPosition(new google.maps.LatLng(latitude, longitude));
28761 getCurrentPosition: function()
28764 latitude: this.gMapContext.location.lat(),
28765 longitude: this.gMapContext.location.lng()
28769 getAddressName: function()
28771 return this.gMapContext.locationName;
28774 getAddressComponents: function()
28776 return this.gMapContext.addressComponents;
28779 address_component_from_google_geocode: function(address_components)
28783 for (var i = 0; i < address_components.length; i++) {
28784 var component = address_components[i];
28785 if (component.types.indexOf("postal_code") >= 0) {
28786 result.postalCode = component.short_name;
28787 } else if (component.types.indexOf("street_number") >= 0) {
28788 result.streetNumber = component.short_name;
28789 } else if (component.types.indexOf("route") >= 0) {
28790 result.streetName = component.short_name;
28791 } else if (component.types.indexOf("neighborhood") >= 0) {
28792 result.city = component.short_name;
28793 } else if (component.types.indexOf("locality") >= 0) {
28794 result.city = component.short_name;
28795 } else if (component.types.indexOf("sublocality") >= 0) {
28796 result.district = component.short_name;
28797 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28798 result.stateOrProvince = component.short_name;
28799 } else if (component.types.indexOf("country") >= 0) {
28800 result.country = component.short_name;
28804 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28805 result.addressLine2 = "";
28809 setZoomLevel: function(zoom)
28811 this.gMapContext.map.setZoom(zoom);
28824 this.fireEvent('show', this);
28835 this.fireEvent('hide', this);
28840 Roo.apply(Roo.bootstrap.LocationPicker, {
28842 OverlayView : function(map, options)
28844 options = options || {};
28851 * @class Roo.bootstrap.Alert
28852 * @extends Roo.bootstrap.Component
28853 * Bootstrap Alert class - shows an alert area box
28855 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28856 Enter a valid email address
28859 * @cfg {String} title The title of alert
28860 * @cfg {String} html The content of alert
28861 * @cfg {String} weight ( success | info | warning | danger )
28862 * @cfg {String} faicon font-awesomeicon
28865 * Create a new alert
28866 * @param {Object} config The config object
28870 Roo.bootstrap.Alert = function(config){
28871 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28875 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28882 getAutoCreate : function()
28891 cls : 'roo-alert-icon'
28896 cls : 'roo-alert-title',
28901 cls : 'roo-alert-text',
28908 cfg.cn[0].cls += ' fa ' + this.faicon;
28912 cfg.cls += ' alert-' + this.weight;
28918 initEvents: function()
28920 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28923 setTitle : function(str)
28925 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28928 setText : function(str)
28930 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28933 setWeight : function(weight)
28936 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28939 this.weight = weight;
28941 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28944 setIcon : function(icon)
28947 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28950 this.faicon = icon;
28952 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28973 * @class Roo.bootstrap.UploadCropbox
28974 * @extends Roo.bootstrap.Component
28975 * Bootstrap UploadCropbox class
28976 * @cfg {String} emptyText show when image has been loaded
28977 * @cfg {String} rotateNotify show when image too small to rotate
28978 * @cfg {Number} errorTimeout default 3000
28979 * @cfg {Number} minWidth default 300
28980 * @cfg {Number} minHeight default 300
28981 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28982 * @cfg {Boolean} isDocument (true|false) default false
28983 * @cfg {String} url action url
28984 * @cfg {String} paramName default 'imageUpload'
28985 * @cfg {String} method default POST
28986 * @cfg {Boolean} loadMask (true|false) default true
28987 * @cfg {Boolean} loadingText default 'Loading...'
28990 * Create a new UploadCropbox
28991 * @param {Object} config The config object
28994 Roo.bootstrap.UploadCropbox = function(config){
28995 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28999 * @event beforeselectfile
29000 * Fire before select file
29001 * @param {Roo.bootstrap.UploadCropbox} this
29003 "beforeselectfile" : true,
29006 * Fire after initEvent
29007 * @param {Roo.bootstrap.UploadCropbox} this
29012 * Fire after initEvent
29013 * @param {Roo.bootstrap.UploadCropbox} this
29014 * @param {String} data
29019 * Fire when preparing the file data
29020 * @param {Roo.bootstrap.UploadCropbox} this
29021 * @param {Object} file
29026 * Fire when get exception
29027 * @param {Roo.bootstrap.UploadCropbox} this
29028 * @param {XMLHttpRequest} xhr
29030 "exception" : true,
29032 * @event beforeloadcanvas
29033 * Fire before load the canvas
29034 * @param {Roo.bootstrap.UploadCropbox} this
29035 * @param {String} src
29037 "beforeloadcanvas" : true,
29040 * Fire when trash image
29041 * @param {Roo.bootstrap.UploadCropbox} this
29046 * Fire when download the image
29047 * @param {Roo.bootstrap.UploadCropbox} this
29051 * @event footerbuttonclick
29052 * Fire when footerbuttonclick
29053 * @param {Roo.bootstrap.UploadCropbox} this
29054 * @param {String} type
29056 "footerbuttonclick" : true,
29060 * @param {Roo.bootstrap.UploadCropbox} this
29065 * Fire when rotate the image
29066 * @param {Roo.bootstrap.UploadCropbox} this
29067 * @param {String} pos
29072 * Fire when inspect the file
29073 * @param {Roo.bootstrap.UploadCropbox} this
29074 * @param {Object} file
29079 * Fire when xhr upload the file
29080 * @param {Roo.bootstrap.UploadCropbox} this
29081 * @param {Object} data
29086 * Fire when arrange the file data
29087 * @param {Roo.bootstrap.UploadCropbox} this
29088 * @param {Object} formData
29093 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29096 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29098 emptyText : 'Click to upload image',
29099 rotateNotify : 'Image is too small to rotate',
29100 errorTimeout : 3000,
29114 cropType : 'image/jpeg',
29116 canvasLoaded : false,
29117 isDocument : false,
29119 paramName : 'imageUpload',
29121 loadingText : 'Loading...',
29124 getAutoCreate : function()
29128 cls : 'roo-upload-cropbox',
29132 cls : 'roo-upload-cropbox-selector',
29137 cls : 'roo-upload-cropbox-body',
29138 style : 'cursor:pointer',
29142 cls : 'roo-upload-cropbox-preview'
29146 cls : 'roo-upload-cropbox-thumb'
29150 cls : 'roo-upload-cropbox-empty-notify',
29151 html : this.emptyText
29155 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29156 html : this.rotateNotify
29162 cls : 'roo-upload-cropbox-footer',
29165 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29175 onRender : function(ct, position)
29177 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29179 if (this.buttons.length) {
29181 Roo.each(this.buttons, function(bb) {
29183 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29185 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29191 this.maskEl = this.el;
29195 initEvents : function()
29197 this.urlAPI = (window.createObjectURL && window) ||
29198 (window.URL && URL.revokeObjectURL && URL) ||
29199 (window.webkitURL && webkitURL);
29201 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29202 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29204 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29205 this.selectorEl.hide();
29207 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29208 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29210 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29211 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29212 this.thumbEl.hide();
29214 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29215 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29217 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29218 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29219 this.errorEl.hide();
29221 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29222 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29223 this.footerEl.hide();
29225 this.setThumbBoxSize();
29231 this.fireEvent('initial', this);
29238 window.addEventListener("resize", function() { _this.resize(); } );
29240 this.bodyEl.on('click', this.beforeSelectFile, this);
29243 this.bodyEl.on('touchstart', this.onTouchStart, this);
29244 this.bodyEl.on('touchmove', this.onTouchMove, this);
29245 this.bodyEl.on('touchend', this.onTouchEnd, this);
29249 this.bodyEl.on('mousedown', this.onMouseDown, this);
29250 this.bodyEl.on('mousemove', this.onMouseMove, this);
29251 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29252 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29253 Roo.get(document).on('mouseup', this.onMouseUp, this);
29256 this.selectorEl.on('change', this.onFileSelected, this);
29262 this.baseScale = 1;
29264 this.baseRotate = 1;
29265 this.dragable = false;
29266 this.pinching = false;
29269 this.cropData = false;
29270 this.notifyEl.dom.innerHTML = this.emptyText;
29272 this.selectorEl.dom.value = '';
29276 resize : function()
29278 if(this.fireEvent('resize', this) != false){
29279 this.setThumbBoxPosition();
29280 this.setCanvasPosition();
29284 onFooterButtonClick : function(e, el, o, type)
29287 case 'rotate-left' :
29288 this.onRotateLeft(e);
29290 case 'rotate-right' :
29291 this.onRotateRight(e);
29294 this.beforeSelectFile(e);
29309 this.fireEvent('footerbuttonclick', this, type);
29312 beforeSelectFile : function(e)
29314 e.preventDefault();
29316 if(this.fireEvent('beforeselectfile', this) != false){
29317 this.selectorEl.dom.click();
29321 onFileSelected : function(e)
29323 e.preventDefault();
29325 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29329 var file = this.selectorEl.dom.files[0];
29331 if(this.fireEvent('inspect', this, file) != false){
29332 this.prepare(file);
29337 trash : function(e)
29339 this.fireEvent('trash', this);
29342 download : function(e)
29344 this.fireEvent('download', this);
29347 loadCanvas : function(src)
29349 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29353 this.imageEl = document.createElement('img');
29357 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29359 this.imageEl.src = src;
29363 onLoadCanvas : function()
29365 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29366 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29368 this.bodyEl.un('click', this.beforeSelectFile, this);
29370 this.notifyEl.hide();
29371 this.thumbEl.show();
29372 this.footerEl.show();
29374 this.baseRotateLevel();
29376 if(this.isDocument){
29377 this.setThumbBoxSize();
29380 this.setThumbBoxPosition();
29382 this.baseScaleLevel();
29388 this.canvasLoaded = true;
29391 this.maskEl.unmask();
29396 setCanvasPosition : function()
29398 if(!this.canvasEl){
29402 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29403 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29405 this.previewEl.setLeft(pw);
29406 this.previewEl.setTop(ph);
29410 onMouseDown : function(e)
29414 this.dragable = true;
29415 this.pinching = false;
29417 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29418 this.dragable = false;
29422 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29423 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29427 onMouseMove : function(e)
29431 if(!this.canvasLoaded){
29435 if (!this.dragable){
29439 var minX = Math.ceil(this.thumbEl.getLeft(true));
29440 var minY = Math.ceil(this.thumbEl.getTop(true));
29442 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29443 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29445 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29446 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29448 x = x - this.mouseX;
29449 y = y - this.mouseY;
29451 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29452 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29454 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29455 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29457 this.previewEl.setLeft(bgX);
29458 this.previewEl.setTop(bgY);
29460 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29461 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29464 onMouseUp : function(e)
29468 this.dragable = false;
29471 onMouseWheel : function(e)
29475 this.startScale = this.scale;
29477 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29479 if(!this.zoomable()){
29480 this.scale = this.startScale;
29489 zoomable : function()
29491 var minScale = this.thumbEl.getWidth() / this.minWidth;
29493 if(this.minWidth < this.minHeight){
29494 minScale = this.thumbEl.getHeight() / this.minHeight;
29497 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29498 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29502 (this.rotate == 0 || this.rotate == 180) &&
29504 width > this.imageEl.OriginWidth ||
29505 height > this.imageEl.OriginHeight ||
29506 (width < this.minWidth && height < this.minHeight)
29514 (this.rotate == 90 || this.rotate == 270) &&
29516 width > this.imageEl.OriginWidth ||
29517 height > this.imageEl.OriginHeight ||
29518 (width < this.minHeight && height < this.minWidth)
29525 !this.isDocument &&
29526 (this.rotate == 0 || this.rotate == 180) &&
29528 width < this.minWidth ||
29529 width > this.imageEl.OriginWidth ||
29530 height < this.minHeight ||
29531 height > this.imageEl.OriginHeight
29538 !this.isDocument &&
29539 (this.rotate == 90 || this.rotate == 270) &&
29541 width < this.minHeight ||
29542 width > this.imageEl.OriginWidth ||
29543 height < this.minWidth ||
29544 height > this.imageEl.OriginHeight
29554 onRotateLeft : function(e)
29556 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29558 var minScale = this.thumbEl.getWidth() / this.minWidth;
29560 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29561 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29563 this.startScale = this.scale;
29565 while (this.getScaleLevel() < minScale){
29567 this.scale = this.scale + 1;
29569 if(!this.zoomable()){
29574 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29575 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29580 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29587 this.scale = this.startScale;
29589 this.onRotateFail();
29594 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29596 if(this.isDocument){
29597 this.setThumbBoxSize();
29598 this.setThumbBoxPosition();
29599 this.setCanvasPosition();
29604 this.fireEvent('rotate', this, 'left');
29608 onRotateRight : function(e)
29610 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29612 var minScale = this.thumbEl.getWidth() / this.minWidth;
29614 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29615 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29617 this.startScale = this.scale;
29619 while (this.getScaleLevel() < minScale){
29621 this.scale = this.scale + 1;
29623 if(!this.zoomable()){
29628 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29629 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29634 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29641 this.scale = this.startScale;
29643 this.onRotateFail();
29648 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29650 if(this.isDocument){
29651 this.setThumbBoxSize();
29652 this.setThumbBoxPosition();
29653 this.setCanvasPosition();
29658 this.fireEvent('rotate', this, 'right');
29661 onRotateFail : function()
29663 this.errorEl.show(true);
29667 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29672 this.previewEl.dom.innerHTML = '';
29674 var canvasEl = document.createElement("canvas");
29676 var contextEl = canvasEl.getContext("2d");
29678 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29679 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29680 var center = this.imageEl.OriginWidth / 2;
29682 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29683 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29684 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29685 center = this.imageEl.OriginHeight / 2;
29688 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29690 contextEl.translate(center, center);
29691 contextEl.rotate(this.rotate * Math.PI / 180);
29693 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29695 this.canvasEl = document.createElement("canvas");
29697 this.contextEl = this.canvasEl.getContext("2d");
29699 switch (this.rotate) {
29702 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29703 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29705 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29710 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29711 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29713 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29714 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);
29718 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29723 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29724 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29726 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29727 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);
29731 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);
29736 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29737 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29739 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29740 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29744 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);
29751 this.previewEl.appendChild(this.canvasEl);
29753 this.setCanvasPosition();
29758 if(!this.canvasLoaded){
29762 var imageCanvas = document.createElement("canvas");
29764 var imageContext = imageCanvas.getContext("2d");
29766 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29767 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29769 var center = imageCanvas.width / 2;
29771 imageContext.translate(center, center);
29773 imageContext.rotate(this.rotate * Math.PI / 180);
29775 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29777 var canvas = document.createElement("canvas");
29779 var context = canvas.getContext("2d");
29781 canvas.width = this.minWidth;
29782 canvas.height = this.minHeight;
29784 switch (this.rotate) {
29787 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29788 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29790 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29791 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29793 var targetWidth = this.minWidth - 2 * x;
29794 var targetHeight = this.minHeight - 2 * y;
29798 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29799 scale = targetWidth / width;
29802 if(x > 0 && y == 0){
29803 scale = targetHeight / height;
29806 if(x > 0 && y > 0){
29807 scale = targetWidth / width;
29809 if(width < height){
29810 scale = targetHeight / height;
29814 context.scale(scale, scale);
29816 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29817 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29819 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29820 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29822 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29827 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29828 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29830 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29831 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29833 var targetWidth = this.minWidth - 2 * x;
29834 var targetHeight = this.minHeight - 2 * y;
29838 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29839 scale = targetWidth / width;
29842 if(x > 0 && y == 0){
29843 scale = targetHeight / height;
29846 if(x > 0 && y > 0){
29847 scale = targetWidth / width;
29849 if(width < height){
29850 scale = targetHeight / height;
29854 context.scale(scale, scale);
29856 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29857 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29859 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29860 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29862 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29864 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29869 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29870 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29872 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29873 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29875 var targetWidth = this.minWidth - 2 * x;
29876 var targetHeight = this.minHeight - 2 * y;
29880 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29881 scale = targetWidth / width;
29884 if(x > 0 && y == 0){
29885 scale = targetHeight / height;
29888 if(x > 0 && y > 0){
29889 scale = targetWidth / width;
29891 if(width < height){
29892 scale = targetHeight / height;
29896 context.scale(scale, scale);
29898 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29899 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29901 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29902 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29904 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29905 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29907 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29912 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29913 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29915 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29916 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29918 var targetWidth = this.minWidth - 2 * x;
29919 var targetHeight = this.minHeight - 2 * y;
29923 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29924 scale = targetWidth / width;
29927 if(x > 0 && y == 0){
29928 scale = targetHeight / height;
29931 if(x > 0 && y > 0){
29932 scale = targetWidth / width;
29934 if(width < height){
29935 scale = targetHeight / height;
29939 context.scale(scale, scale);
29941 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29942 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29944 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29945 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29947 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29949 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29956 this.cropData = canvas.toDataURL(this.cropType);
29958 if(this.fireEvent('crop', this, this.cropData) !== false){
29959 this.process(this.file, this.cropData);
29966 setThumbBoxSize : function()
29970 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29971 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29972 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29974 this.minWidth = width;
29975 this.minHeight = height;
29977 if(this.rotate == 90 || this.rotate == 270){
29978 this.minWidth = height;
29979 this.minHeight = width;
29984 width = Math.ceil(this.minWidth * height / this.minHeight);
29986 if(this.minWidth > this.minHeight){
29988 height = Math.ceil(this.minHeight * width / this.minWidth);
29991 this.thumbEl.setStyle({
29992 width : width + 'px',
29993 height : height + 'px'
30000 setThumbBoxPosition : function()
30002 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30003 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30005 this.thumbEl.setLeft(x);
30006 this.thumbEl.setTop(y);
30010 baseRotateLevel : function()
30012 this.baseRotate = 1;
30015 typeof(this.exif) != 'undefined' &&
30016 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30017 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30019 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30022 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30026 baseScaleLevel : function()
30030 if(this.isDocument){
30032 if(this.baseRotate == 6 || this.baseRotate == 8){
30034 height = this.thumbEl.getHeight();
30035 this.baseScale = height / this.imageEl.OriginWidth;
30037 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30038 width = this.thumbEl.getWidth();
30039 this.baseScale = width / this.imageEl.OriginHeight;
30045 height = this.thumbEl.getHeight();
30046 this.baseScale = height / this.imageEl.OriginHeight;
30048 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30049 width = this.thumbEl.getWidth();
30050 this.baseScale = width / this.imageEl.OriginWidth;
30056 if(this.baseRotate == 6 || this.baseRotate == 8){
30058 width = this.thumbEl.getHeight();
30059 this.baseScale = width / this.imageEl.OriginHeight;
30061 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30062 height = this.thumbEl.getWidth();
30063 this.baseScale = height / this.imageEl.OriginHeight;
30066 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30067 height = this.thumbEl.getWidth();
30068 this.baseScale = height / this.imageEl.OriginHeight;
30070 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30071 width = this.thumbEl.getHeight();
30072 this.baseScale = width / this.imageEl.OriginWidth;
30079 width = this.thumbEl.getWidth();
30080 this.baseScale = width / this.imageEl.OriginWidth;
30082 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30083 height = this.thumbEl.getHeight();
30084 this.baseScale = height / this.imageEl.OriginHeight;
30087 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30089 height = this.thumbEl.getHeight();
30090 this.baseScale = height / this.imageEl.OriginHeight;
30092 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30093 width = this.thumbEl.getWidth();
30094 this.baseScale = width / this.imageEl.OriginWidth;
30102 getScaleLevel : function()
30104 return this.baseScale * Math.pow(1.1, this.scale);
30107 onTouchStart : function(e)
30109 if(!this.canvasLoaded){
30110 this.beforeSelectFile(e);
30114 var touches = e.browserEvent.touches;
30120 if(touches.length == 1){
30121 this.onMouseDown(e);
30125 if(touches.length != 2){
30131 for(var i = 0, finger; finger = touches[i]; i++){
30132 coords.push(finger.pageX, finger.pageY);
30135 var x = Math.pow(coords[0] - coords[2], 2);
30136 var y = Math.pow(coords[1] - coords[3], 2);
30138 this.startDistance = Math.sqrt(x + y);
30140 this.startScale = this.scale;
30142 this.pinching = true;
30143 this.dragable = false;
30147 onTouchMove : function(e)
30149 if(!this.pinching && !this.dragable){
30153 var touches = e.browserEvent.touches;
30160 this.onMouseMove(e);
30166 for(var i = 0, finger; finger = touches[i]; i++){
30167 coords.push(finger.pageX, finger.pageY);
30170 var x = Math.pow(coords[0] - coords[2], 2);
30171 var y = Math.pow(coords[1] - coords[3], 2);
30173 this.endDistance = Math.sqrt(x + y);
30175 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30177 if(!this.zoomable()){
30178 this.scale = this.startScale;
30186 onTouchEnd : function(e)
30188 this.pinching = false;
30189 this.dragable = false;
30193 process : function(file, crop)
30196 this.maskEl.mask(this.loadingText);
30199 this.xhr = new XMLHttpRequest();
30201 file.xhr = this.xhr;
30203 this.xhr.open(this.method, this.url, true);
30206 "Accept": "application/json",
30207 "Cache-Control": "no-cache",
30208 "X-Requested-With": "XMLHttpRequest"
30211 for (var headerName in headers) {
30212 var headerValue = headers[headerName];
30214 this.xhr.setRequestHeader(headerName, headerValue);
30220 this.xhr.onload = function()
30222 _this.xhrOnLoad(_this.xhr);
30225 this.xhr.onerror = function()
30227 _this.xhrOnError(_this.xhr);
30230 var formData = new FormData();
30232 formData.append('returnHTML', 'NO');
30235 formData.append('crop', crop);
30238 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30239 formData.append(this.paramName, file, file.name);
30242 if(typeof(file.filename) != 'undefined'){
30243 formData.append('filename', file.filename);
30246 if(typeof(file.mimetype) != 'undefined'){
30247 formData.append('mimetype', file.mimetype);
30250 if(this.fireEvent('arrange', this, formData) != false){
30251 this.xhr.send(formData);
30255 xhrOnLoad : function(xhr)
30258 this.maskEl.unmask();
30261 if (xhr.readyState !== 4) {
30262 this.fireEvent('exception', this, xhr);
30266 var response = Roo.decode(xhr.responseText);
30268 if(!response.success){
30269 this.fireEvent('exception', this, xhr);
30273 var response = Roo.decode(xhr.responseText);
30275 this.fireEvent('upload', this, response);
30279 xhrOnError : function()
30282 this.maskEl.unmask();
30285 Roo.log('xhr on error');
30287 var response = Roo.decode(xhr.responseText);
30293 prepare : function(file)
30296 this.maskEl.mask(this.loadingText);
30302 if(typeof(file) === 'string'){
30303 this.loadCanvas(file);
30307 if(!file || !this.urlAPI){
30312 this.cropType = file.type;
30316 if(this.fireEvent('prepare', this, this.file) != false){
30318 var reader = new FileReader();
30320 reader.onload = function (e) {
30321 if (e.target.error) {
30322 Roo.log(e.target.error);
30326 var buffer = e.target.result,
30327 dataView = new DataView(buffer),
30329 maxOffset = dataView.byteLength - 4,
30333 if (dataView.getUint16(0) === 0xffd8) {
30334 while (offset < maxOffset) {
30335 markerBytes = dataView.getUint16(offset);
30337 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30338 markerLength = dataView.getUint16(offset + 2) + 2;
30339 if (offset + markerLength > dataView.byteLength) {
30340 Roo.log('Invalid meta data: Invalid segment size.');
30344 if(markerBytes == 0xffe1){
30345 _this.parseExifData(
30352 offset += markerLength;
30362 var url = _this.urlAPI.createObjectURL(_this.file);
30364 _this.loadCanvas(url);
30369 reader.readAsArrayBuffer(this.file);
30375 parseExifData : function(dataView, offset, length)
30377 var tiffOffset = offset + 10,
30381 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30382 // No Exif data, might be XMP data instead
30386 // Check for the ASCII code for "Exif" (0x45786966):
30387 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30388 // No Exif data, might be XMP data instead
30391 if (tiffOffset + 8 > dataView.byteLength) {
30392 Roo.log('Invalid Exif data: Invalid segment size.');
30395 // Check for the two null bytes:
30396 if (dataView.getUint16(offset + 8) !== 0x0000) {
30397 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30400 // Check the byte alignment:
30401 switch (dataView.getUint16(tiffOffset)) {
30403 littleEndian = true;
30406 littleEndian = false;
30409 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30412 // Check for the TIFF tag marker (0x002A):
30413 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30414 Roo.log('Invalid Exif data: Missing TIFF marker.');
30417 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30418 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30420 this.parseExifTags(
30423 tiffOffset + dirOffset,
30428 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30433 if (dirOffset + 6 > dataView.byteLength) {
30434 Roo.log('Invalid Exif data: Invalid directory offset.');
30437 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30438 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30439 if (dirEndOffset + 4 > dataView.byteLength) {
30440 Roo.log('Invalid Exif data: Invalid directory size.');
30443 for (i = 0; i < tagsNumber; i += 1) {
30447 dirOffset + 2 + 12 * i, // tag offset
30451 // Return the offset to the next directory:
30452 return dataView.getUint32(dirEndOffset, littleEndian);
30455 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30457 var tag = dataView.getUint16(offset, littleEndian);
30459 this.exif[tag] = this.getExifValue(
30463 dataView.getUint16(offset + 2, littleEndian), // tag type
30464 dataView.getUint32(offset + 4, littleEndian), // tag length
30469 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30471 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30480 Roo.log('Invalid Exif data: Invalid tag type.');
30484 tagSize = tagType.size * length;
30485 // Determine if the value is contained in the dataOffset bytes,
30486 // or if the value at the dataOffset is a pointer to the actual data:
30487 dataOffset = tagSize > 4 ?
30488 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30489 if (dataOffset + tagSize > dataView.byteLength) {
30490 Roo.log('Invalid Exif data: Invalid data offset.');
30493 if (length === 1) {
30494 return tagType.getValue(dataView, dataOffset, littleEndian);
30497 for (i = 0; i < length; i += 1) {
30498 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30501 if (tagType.ascii) {
30503 // Concatenate the chars:
30504 for (i = 0; i < values.length; i += 1) {
30506 // Ignore the terminating NULL byte(s):
30507 if (c === '\u0000') {
30519 Roo.apply(Roo.bootstrap.UploadCropbox, {
30521 'Orientation': 0x0112
30525 1: 0, //'top-left',
30527 3: 180, //'bottom-right',
30528 // 4: 'bottom-left',
30530 6: 90, //'right-top',
30531 // 7: 'right-bottom',
30532 8: 270 //'left-bottom'
30536 // byte, 8-bit unsigned int:
30538 getValue: function (dataView, dataOffset) {
30539 return dataView.getUint8(dataOffset);
30543 // ascii, 8-bit byte:
30545 getValue: function (dataView, dataOffset) {
30546 return String.fromCharCode(dataView.getUint8(dataOffset));
30551 // short, 16 bit int:
30553 getValue: function (dataView, dataOffset, littleEndian) {
30554 return dataView.getUint16(dataOffset, littleEndian);
30558 // long, 32 bit int:
30560 getValue: function (dataView, dataOffset, littleEndian) {
30561 return dataView.getUint32(dataOffset, littleEndian);
30565 // rational = two long values, first is numerator, second is denominator:
30567 getValue: function (dataView, dataOffset, littleEndian) {
30568 return dataView.getUint32(dataOffset, littleEndian) /
30569 dataView.getUint32(dataOffset + 4, littleEndian);
30573 // slong, 32 bit signed int:
30575 getValue: function (dataView, dataOffset, littleEndian) {
30576 return dataView.getInt32(dataOffset, littleEndian);
30580 // srational, two slongs, first is numerator, second is denominator:
30582 getValue: function (dataView, dataOffset, littleEndian) {
30583 return dataView.getInt32(dataOffset, littleEndian) /
30584 dataView.getInt32(dataOffset + 4, littleEndian);
30594 cls : 'btn-group roo-upload-cropbox-rotate-left',
30595 action : 'rotate-left',
30599 cls : 'btn btn-default',
30600 html : '<i class="fa fa-undo"></i>'
30606 cls : 'btn-group roo-upload-cropbox-picture',
30607 action : 'picture',
30611 cls : 'btn btn-default',
30612 html : '<i class="fa fa-picture-o"></i>'
30618 cls : 'btn-group roo-upload-cropbox-rotate-right',
30619 action : 'rotate-right',
30623 cls : 'btn btn-default',
30624 html : '<i class="fa fa-repeat"></i>'
30632 cls : 'btn-group roo-upload-cropbox-rotate-left',
30633 action : 'rotate-left',
30637 cls : 'btn btn-default',
30638 html : '<i class="fa fa-undo"></i>'
30644 cls : 'btn-group roo-upload-cropbox-download',
30645 action : 'download',
30649 cls : 'btn btn-default',
30650 html : '<i class="fa fa-download"></i>'
30656 cls : 'btn-group roo-upload-cropbox-crop',
30661 cls : 'btn btn-default',
30662 html : '<i class="fa fa-crop"></i>'
30668 cls : 'btn-group roo-upload-cropbox-trash',
30673 cls : 'btn btn-default',
30674 html : '<i class="fa fa-trash"></i>'
30680 cls : 'btn-group roo-upload-cropbox-rotate-right',
30681 action : 'rotate-right',
30685 cls : 'btn btn-default',
30686 html : '<i class="fa fa-repeat"></i>'
30694 cls : 'btn-group roo-upload-cropbox-rotate-left',
30695 action : 'rotate-left',
30699 cls : 'btn btn-default',
30700 html : '<i class="fa fa-undo"></i>'
30706 cls : 'btn-group roo-upload-cropbox-rotate-right',
30707 action : 'rotate-right',
30711 cls : 'btn btn-default',
30712 html : '<i class="fa fa-repeat"></i>'
30725 * @class Roo.bootstrap.DocumentManager
30726 * @extends Roo.bootstrap.Component
30727 * Bootstrap DocumentManager class
30728 * @cfg {String} paramName default 'imageUpload'
30729 * @cfg {String} toolTipName default 'filename'
30730 * @cfg {String} method default POST
30731 * @cfg {String} url action url
30732 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30733 * @cfg {Boolean} multiple multiple upload default true
30734 * @cfg {Number} thumbSize default 300
30735 * @cfg {String} fieldLabel
30736 * @cfg {Number} labelWidth default 4
30737 * @cfg {String} labelAlign (left|top) default left
30738 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30739 * @cfg {Number} labellg set the width of label (1-12)
30740 * @cfg {Number} labelmd set the width of label (1-12)
30741 * @cfg {Number} labelsm set the width of label (1-12)
30742 * @cfg {Number} labelxs set the width of label (1-12)
30745 * Create a new DocumentManager
30746 * @param {Object} config The config object
30749 Roo.bootstrap.DocumentManager = function(config){
30750 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30753 this.delegates = [];
30758 * Fire when initial the DocumentManager
30759 * @param {Roo.bootstrap.DocumentManager} this
30764 * inspect selected file
30765 * @param {Roo.bootstrap.DocumentManager} this
30766 * @param {File} file
30771 * Fire when xhr load exception
30772 * @param {Roo.bootstrap.DocumentManager} this
30773 * @param {XMLHttpRequest} xhr
30775 "exception" : true,
30777 * @event afterupload
30778 * Fire when xhr load exception
30779 * @param {Roo.bootstrap.DocumentManager} this
30780 * @param {XMLHttpRequest} xhr
30782 "afterupload" : true,
30785 * prepare the form data
30786 * @param {Roo.bootstrap.DocumentManager} this
30787 * @param {Object} formData
30792 * Fire when remove the file
30793 * @param {Roo.bootstrap.DocumentManager} this
30794 * @param {Object} file
30799 * Fire after refresh the file
30800 * @param {Roo.bootstrap.DocumentManager} this
30805 * Fire after click the image
30806 * @param {Roo.bootstrap.DocumentManager} this
30807 * @param {Object} file
30812 * Fire when upload a image and editable set to true
30813 * @param {Roo.bootstrap.DocumentManager} this
30814 * @param {Object} file
30818 * @event beforeselectfile
30819 * Fire before select file
30820 * @param {Roo.bootstrap.DocumentManager} this
30822 "beforeselectfile" : true,
30825 * Fire before process file
30826 * @param {Roo.bootstrap.DocumentManager} this
30827 * @param {Object} file
30831 * @event previewrendered
30832 * Fire when preview rendered
30833 * @param {Roo.bootstrap.DocumentManager} this
30834 * @param {Object} file
30836 "previewrendered" : true,
30839 "previewResize" : true
30844 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30853 paramName : 'imageUpload',
30854 toolTipName : 'filename',
30857 labelAlign : 'left',
30867 getAutoCreate : function()
30869 var managerWidget = {
30871 cls : 'roo-document-manager',
30875 cls : 'roo-document-manager-selector',
30880 cls : 'roo-document-manager-uploader',
30884 cls : 'roo-document-manager-upload-btn',
30885 html : '<i class="fa fa-plus"></i>'
30896 cls : 'column col-md-12',
30901 if(this.fieldLabel.length){
30906 cls : 'column col-md-12',
30907 html : this.fieldLabel
30911 cls : 'column col-md-12',
30916 if(this.labelAlign == 'left'){
30921 html : this.fieldLabel
30930 if(this.labelWidth > 12){
30931 content[0].style = "width: " + this.labelWidth + 'px';
30934 if(this.labelWidth < 13 && this.labelmd == 0){
30935 this.labelmd = this.labelWidth;
30938 if(this.labellg > 0){
30939 content[0].cls += ' col-lg-' + this.labellg;
30940 content[1].cls += ' col-lg-' + (12 - this.labellg);
30943 if(this.labelmd > 0){
30944 content[0].cls += ' col-md-' + this.labelmd;
30945 content[1].cls += ' col-md-' + (12 - this.labelmd);
30948 if(this.labelsm > 0){
30949 content[0].cls += ' col-sm-' + this.labelsm;
30950 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30953 if(this.labelxs > 0){
30954 content[0].cls += ' col-xs-' + this.labelxs;
30955 content[1].cls += ' col-xs-' + (12 - this.labelxs);
30963 cls : 'row clearfix',
30971 initEvents : function()
30973 this.managerEl = this.el.select('.roo-document-manager', true).first();
30974 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30976 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30977 this.selectorEl.hide();
30980 this.selectorEl.attr('multiple', 'multiple');
30983 this.selectorEl.on('change', this.onFileSelected, this);
30985 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30986 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30988 this.uploader.on('click', this.onUploaderClick, this);
30990 this.renderProgressDialog();
30994 window.addEventListener("resize", function() { _this.refresh(); } );
30996 this.fireEvent('initial', this);
30999 renderProgressDialog : function()
31003 this.progressDialog = new Roo.bootstrap.Modal({
31004 cls : 'roo-document-manager-progress-dialog',
31005 allow_close : false,
31016 btnclick : function() {
31017 _this.uploadCancel();
31023 this.progressDialog.render(Roo.get(document.body));
31025 this.progress = new Roo.bootstrap.Progress({
31026 cls : 'roo-document-manager-progress',
31031 this.progress.render(this.progressDialog.getChildContainer());
31033 this.progressBar = new Roo.bootstrap.ProgressBar({
31034 cls : 'roo-document-manager-progress-bar',
31037 aria_valuemax : 12,
31041 this.progressBar.render(this.progress.getChildContainer());
31044 onUploaderClick : function(e)
31046 e.preventDefault();
31048 if(this.fireEvent('beforeselectfile', this) != false){
31049 this.selectorEl.dom.click();
31054 onFileSelected : function(e)
31056 e.preventDefault();
31058 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31062 Roo.each(this.selectorEl.dom.files, function(file){
31063 if(this.fireEvent('inspect', this, file) != false){
31064 this.files.push(file);
31074 this.selectorEl.dom.value = '';
31076 if(!this.files || !this.files.length){
31080 if(this.boxes > 0 && this.files.length > this.boxes){
31081 this.files = this.files.slice(0, this.boxes);
31084 this.uploader.show();
31086 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31087 this.uploader.hide();
31096 Roo.each(this.files, function(file){
31098 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31099 var f = this.renderPreview(file);
31104 if(file.type.indexOf('image') != -1){
31105 this.delegates.push(
31107 _this.process(file);
31108 }).createDelegate(this)
31116 _this.process(file);
31117 }).createDelegate(this)
31122 this.files = files;
31124 this.delegates = this.delegates.concat(docs);
31126 if(!this.delegates.length){
31131 this.progressBar.aria_valuemax = this.delegates.length;
31138 arrange : function()
31140 if(!this.delegates.length){
31141 this.progressDialog.hide();
31146 var delegate = this.delegates.shift();
31148 this.progressDialog.show();
31150 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31152 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31157 refresh : function()
31159 this.uploader.show();
31161 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31162 this.uploader.hide();
31165 Roo.isTouch ? this.closable(false) : this.closable(true);
31167 this.fireEvent('refresh', this);
31170 onRemove : function(e, el, o)
31172 e.preventDefault();
31174 this.fireEvent('remove', this, o);
31178 remove : function(o)
31182 Roo.each(this.files, function(file){
31183 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31192 this.files = files;
31199 Roo.each(this.files, function(file){
31204 file.target.remove();
31213 onClick : function(e, el, o)
31215 e.preventDefault();
31217 this.fireEvent('click', this, o);
31221 closable : function(closable)
31223 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31225 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31237 xhrOnLoad : function(xhr)
31239 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31243 if (xhr.readyState !== 4) {
31245 this.fireEvent('exception', this, xhr);
31249 var response = Roo.decode(xhr.responseText);
31251 if(!response.success){
31253 this.fireEvent('exception', this, xhr);
31257 var file = this.renderPreview(response.data);
31259 this.files.push(file);
31263 this.fireEvent('afterupload', this, xhr);
31267 xhrOnError : function(xhr)
31269 Roo.log('xhr on error');
31271 var response = Roo.decode(xhr.responseText);
31278 process : function(file)
31280 if(this.fireEvent('process', this, file) !== false){
31281 if(this.editable && file.type.indexOf('image') != -1){
31282 this.fireEvent('edit', this, file);
31286 this.uploadStart(file, false);
31293 uploadStart : function(file, crop)
31295 this.xhr = new XMLHttpRequest();
31297 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31302 file.xhr = this.xhr;
31304 this.managerEl.createChild({
31306 cls : 'roo-document-manager-loading',
31310 tooltip : file.name,
31311 cls : 'roo-document-manager-thumb',
31312 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31318 this.xhr.open(this.method, this.url, true);
31321 "Accept": "application/json",
31322 "Cache-Control": "no-cache",
31323 "X-Requested-With": "XMLHttpRequest"
31326 for (var headerName in headers) {
31327 var headerValue = headers[headerName];
31329 this.xhr.setRequestHeader(headerName, headerValue);
31335 this.xhr.onload = function()
31337 _this.xhrOnLoad(_this.xhr);
31340 this.xhr.onerror = function()
31342 _this.xhrOnError(_this.xhr);
31345 var formData = new FormData();
31347 formData.append('returnHTML', 'NO');
31350 formData.append('crop', crop);
31353 formData.append(this.paramName, file, file.name);
31360 if(this.fireEvent('prepare', this, formData, options) != false){
31362 if(options.manually){
31366 this.xhr.send(formData);
31370 this.uploadCancel();
31373 uploadCancel : function()
31379 this.delegates = [];
31381 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31388 renderPreview : function(file)
31390 if(typeof(file.target) != 'undefined' && file.target){
31394 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31396 var previewEl = this.managerEl.createChild({
31398 cls : 'roo-document-manager-preview',
31402 tooltip : file[this.toolTipName],
31403 cls : 'roo-document-manager-thumb',
31404 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31409 html : '<i class="fa fa-times-circle"></i>'
31414 var close = previewEl.select('button.close', true).first();
31416 close.on('click', this.onRemove, this, file);
31418 file.target = previewEl;
31420 var image = previewEl.select('img', true).first();
31424 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31426 image.on('click', this.onClick, this, file);
31428 this.fireEvent('previewrendered', this, file);
31434 onPreviewLoad : function(file, image)
31436 if(typeof(file.target) == 'undefined' || !file.target){
31440 var width = image.dom.naturalWidth || image.dom.width;
31441 var height = image.dom.naturalHeight || image.dom.height;
31443 if(!this.previewResize) {
31447 if(width > height){
31448 file.target.addClass('wide');
31452 file.target.addClass('tall');
31457 uploadFromSource : function(file, crop)
31459 this.xhr = new XMLHttpRequest();
31461 this.managerEl.createChild({
31463 cls : 'roo-document-manager-loading',
31467 tooltip : file.name,
31468 cls : 'roo-document-manager-thumb',
31469 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31475 this.xhr.open(this.method, this.url, true);
31478 "Accept": "application/json",
31479 "Cache-Control": "no-cache",
31480 "X-Requested-With": "XMLHttpRequest"
31483 for (var headerName in headers) {
31484 var headerValue = headers[headerName];
31486 this.xhr.setRequestHeader(headerName, headerValue);
31492 this.xhr.onload = function()
31494 _this.xhrOnLoad(_this.xhr);
31497 this.xhr.onerror = function()
31499 _this.xhrOnError(_this.xhr);
31502 var formData = new FormData();
31504 formData.append('returnHTML', 'NO');
31506 formData.append('crop', crop);
31508 if(typeof(file.filename) != 'undefined'){
31509 formData.append('filename', file.filename);
31512 if(typeof(file.mimetype) != 'undefined'){
31513 formData.append('mimetype', file.mimetype);
31518 if(this.fireEvent('prepare', this, formData) != false){
31519 this.xhr.send(formData);
31529 * @class Roo.bootstrap.DocumentViewer
31530 * @extends Roo.bootstrap.Component
31531 * Bootstrap DocumentViewer class
31532 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31533 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31536 * Create a new DocumentViewer
31537 * @param {Object} config The config object
31540 Roo.bootstrap.DocumentViewer = function(config){
31541 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31546 * Fire after initEvent
31547 * @param {Roo.bootstrap.DocumentViewer} this
31553 * @param {Roo.bootstrap.DocumentViewer} this
31558 * Fire after download button
31559 * @param {Roo.bootstrap.DocumentViewer} this
31564 * Fire after trash button
31565 * @param {Roo.bootstrap.DocumentViewer} this
31572 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31574 showDownload : true,
31578 getAutoCreate : function()
31582 cls : 'roo-document-viewer',
31586 cls : 'roo-document-viewer-body',
31590 cls : 'roo-document-viewer-thumb',
31594 cls : 'roo-document-viewer-image'
31602 cls : 'roo-document-viewer-footer',
31605 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31609 cls : 'btn-group roo-document-viewer-download',
31613 cls : 'btn btn-default',
31614 html : '<i class="fa fa-download"></i>'
31620 cls : 'btn-group roo-document-viewer-trash',
31624 cls : 'btn btn-default',
31625 html : '<i class="fa fa-trash"></i>'
31638 initEvents : function()
31640 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31641 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31643 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31644 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31646 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31647 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31649 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31650 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31652 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31653 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31655 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31656 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31658 this.bodyEl.on('click', this.onClick, this);
31659 this.downloadBtn.on('click', this.onDownload, this);
31660 this.trashBtn.on('click', this.onTrash, this);
31662 this.downloadBtn.hide();
31663 this.trashBtn.hide();
31665 if(this.showDownload){
31666 this.downloadBtn.show();
31669 if(this.showTrash){
31670 this.trashBtn.show();
31673 if(!this.showDownload && !this.showTrash) {
31674 this.footerEl.hide();
31679 initial : function()
31681 this.fireEvent('initial', this);
31685 onClick : function(e)
31687 e.preventDefault();
31689 this.fireEvent('click', this);
31692 onDownload : function(e)
31694 e.preventDefault();
31696 this.fireEvent('download', this);
31699 onTrash : function(e)
31701 e.preventDefault();
31703 this.fireEvent('trash', this);
31715 * @class Roo.bootstrap.NavProgressBar
31716 * @extends Roo.bootstrap.Component
31717 * Bootstrap NavProgressBar class
31720 * Create a new nav progress bar
31721 * @param {Object} config The config object
31724 Roo.bootstrap.NavProgressBar = function(config){
31725 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31727 this.bullets = this.bullets || [];
31729 // Roo.bootstrap.NavProgressBar.register(this);
31733 * Fires when the active item changes
31734 * @param {Roo.bootstrap.NavProgressBar} this
31735 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31736 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31743 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31748 getAutoCreate : function()
31750 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31754 cls : 'roo-navigation-bar-group',
31758 cls : 'roo-navigation-top-bar'
31762 cls : 'roo-navigation-bullets-bar',
31766 cls : 'roo-navigation-bar'
31773 cls : 'roo-navigation-bottom-bar'
31783 initEvents: function()
31788 onRender : function(ct, position)
31790 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31792 if(this.bullets.length){
31793 Roo.each(this.bullets, function(b){
31802 addItem : function(cfg)
31804 var item = new Roo.bootstrap.NavProgressItem(cfg);
31806 item.parentId = this.id;
31807 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31810 var top = new Roo.bootstrap.Element({
31812 cls : 'roo-navigation-bar-text'
31815 var bottom = new Roo.bootstrap.Element({
31817 cls : 'roo-navigation-bar-text'
31820 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31821 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31823 var topText = new Roo.bootstrap.Element({
31825 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31828 var bottomText = new Roo.bootstrap.Element({
31830 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31833 topText.onRender(top.el, null);
31834 bottomText.onRender(bottom.el, null);
31837 item.bottomEl = bottom;
31840 this.barItems.push(item);
31845 getActive : function()
31847 var active = false;
31849 Roo.each(this.barItems, function(v){
31851 if (!v.isActive()) {
31863 setActiveItem : function(item)
31867 Roo.each(this.barItems, function(v){
31868 if (v.rid == item.rid) {
31872 if (v.isActive()) {
31873 v.setActive(false);
31878 item.setActive(true);
31880 this.fireEvent('changed', this, item, prev);
31883 getBarItem: function(rid)
31887 Roo.each(this.barItems, function(e) {
31888 if (e.rid != rid) {
31899 indexOfItem : function(item)
31903 Roo.each(this.barItems, function(v, i){
31905 if (v.rid != item.rid) {
31916 setActiveNext : function()
31918 var i = this.indexOfItem(this.getActive());
31920 if (i > this.barItems.length) {
31924 this.setActiveItem(this.barItems[i+1]);
31927 setActivePrev : function()
31929 var i = this.indexOfItem(this.getActive());
31935 this.setActiveItem(this.barItems[i-1]);
31938 format : function()
31940 if(!this.barItems.length){
31944 var width = 100 / this.barItems.length;
31946 Roo.each(this.barItems, function(i){
31947 i.el.setStyle('width', width + '%');
31948 i.topEl.el.setStyle('width', width + '%');
31949 i.bottomEl.el.setStyle('width', width + '%');
31958 * Nav Progress Item
31963 * @class Roo.bootstrap.NavProgressItem
31964 * @extends Roo.bootstrap.Component
31965 * Bootstrap NavProgressItem class
31966 * @cfg {String} rid the reference id
31967 * @cfg {Boolean} active (true|false) Is item active default false
31968 * @cfg {Boolean} disabled (true|false) Is item active default false
31969 * @cfg {String} html
31970 * @cfg {String} position (top|bottom) text position default bottom
31971 * @cfg {String} icon show icon instead of number
31974 * Create a new NavProgressItem
31975 * @param {Object} config The config object
31977 Roo.bootstrap.NavProgressItem = function(config){
31978 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31983 * The raw click event for the entire grid.
31984 * @param {Roo.bootstrap.NavProgressItem} this
31985 * @param {Roo.EventObject} e
31992 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
31998 position : 'bottom',
32001 getAutoCreate : function()
32003 var iconCls = 'roo-navigation-bar-item-icon';
32005 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32009 cls: 'roo-navigation-bar-item',
32019 cfg.cls += ' active';
32022 cfg.cls += ' disabled';
32028 disable : function()
32030 this.setDisabled(true);
32033 enable : function()
32035 this.setDisabled(false);
32038 initEvents: function()
32040 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32042 this.iconEl.on('click', this.onClick, this);
32045 onClick : function(e)
32047 e.preventDefault();
32053 if(this.fireEvent('click', this, e) === false){
32057 this.parent().setActiveItem(this);
32060 isActive: function ()
32062 return this.active;
32065 setActive : function(state)
32067 if(this.active == state){
32071 this.active = state;
32074 this.el.addClass('active');
32078 this.el.removeClass('active');
32083 setDisabled : function(state)
32085 if(this.disabled == state){
32089 this.disabled = state;
32092 this.el.addClass('disabled');
32096 this.el.removeClass('disabled');
32099 tooltipEl : function()
32101 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32114 * @class Roo.bootstrap.FieldLabel
32115 * @extends Roo.bootstrap.Component
32116 * Bootstrap FieldLabel class
32117 * @cfg {String} html contents of the element
32118 * @cfg {String} tag tag of the element default label
32119 * @cfg {String} cls class of the element
32120 * @cfg {String} target label target
32121 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32122 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32123 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32124 * @cfg {String} iconTooltip default "This field is required"
32125 * @cfg {String} indicatorpos (left|right) default left
32128 * Create a new FieldLabel
32129 * @param {Object} config The config object
32132 Roo.bootstrap.FieldLabel = function(config){
32133 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32138 * Fires after the field has been marked as invalid.
32139 * @param {Roo.form.FieldLabel} this
32140 * @param {String} msg The validation message
32145 * Fires after the field has been validated with no errors.
32146 * @param {Roo.form.FieldLabel} this
32152 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32159 invalidClass : 'has-warning',
32160 validClass : 'has-success',
32161 iconTooltip : 'This field is required',
32162 indicatorpos : 'left',
32164 getAutoCreate : function(){
32167 if (!this.allowBlank) {
32173 cls : 'roo-bootstrap-field-label ' + this.cls,
32178 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32179 tooltip : this.iconTooltip
32188 if(this.indicatorpos == 'right'){
32191 cls : 'roo-bootstrap-field-label ' + this.cls,
32200 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32201 tooltip : this.iconTooltip
32210 initEvents: function()
32212 Roo.bootstrap.Element.superclass.initEvents.call(this);
32214 this.indicator = this.indicatorEl();
32216 if(this.indicator){
32217 this.indicator.removeClass('visible');
32218 this.indicator.addClass('invisible');
32221 Roo.bootstrap.FieldLabel.register(this);
32224 indicatorEl : function()
32226 var indicator = this.el.select('i.roo-required-indicator',true).first();
32237 * Mark this field as valid
32239 markValid : function()
32241 if(this.indicator){
32242 this.indicator.removeClass('visible');
32243 this.indicator.addClass('invisible');
32245 if (Roo.bootstrap.version == 3) {
32246 this.el.removeClass(this.invalidClass);
32247 this.el.addClass(this.validClass);
32249 this.el.removeClass('is-invalid');
32250 this.el.addClass('is-valid');
32254 this.fireEvent('valid', this);
32258 * Mark this field as invalid
32259 * @param {String} msg The validation message
32261 markInvalid : function(msg)
32263 if(this.indicator){
32264 this.indicator.removeClass('invisible');
32265 this.indicator.addClass('visible');
32267 if (Roo.bootstrap.version == 3) {
32268 this.el.removeClass(this.validClass);
32269 this.el.addClass(this.invalidClass);
32271 this.el.removeClass('is-valid');
32272 this.el.addClass('is-invalid');
32276 this.fireEvent('invalid', this, msg);
32282 Roo.apply(Roo.bootstrap.FieldLabel, {
32287 * register a FieldLabel Group
32288 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32290 register : function(label)
32292 if(this.groups.hasOwnProperty(label.target)){
32296 this.groups[label.target] = label;
32300 * fetch a FieldLabel Group based on the target
32301 * @param {string} target
32302 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32304 get: function(target) {
32305 if (typeof(this.groups[target]) == 'undefined') {
32309 return this.groups[target] ;
32318 * page DateSplitField.
32324 * @class Roo.bootstrap.DateSplitField
32325 * @extends Roo.bootstrap.Component
32326 * Bootstrap DateSplitField class
32327 * @cfg {string} fieldLabel - the label associated
32328 * @cfg {Number} labelWidth set the width of label (0-12)
32329 * @cfg {String} labelAlign (top|left)
32330 * @cfg {Boolean} dayAllowBlank (true|false) default false
32331 * @cfg {Boolean} monthAllowBlank (true|false) default false
32332 * @cfg {Boolean} yearAllowBlank (true|false) default false
32333 * @cfg {string} dayPlaceholder
32334 * @cfg {string} monthPlaceholder
32335 * @cfg {string} yearPlaceholder
32336 * @cfg {string} dayFormat default 'd'
32337 * @cfg {string} monthFormat default 'm'
32338 * @cfg {string} yearFormat default 'Y'
32339 * @cfg {Number} labellg set the width of label (1-12)
32340 * @cfg {Number} labelmd set the width of label (1-12)
32341 * @cfg {Number} labelsm set the width of label (1-12)
32342 * @cfg {Number} labelxs set the width of label (1-12)
32346 * Create a new DateSplitField
32347 * @param {Object} config The config object
32350 Roo.bootstrap.DateSplitField = function(config){
32351 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32357 * getting the data of years
32358 * @param {Roo.bootstrap.DateSplitField} this
32359 * @param {Object} years
32364 * getting the data of days
32365 * @param {Roo.bootstrap.DateSplitField} this
32366 * @param {Object} days
32371 * Fires after the field has been marked as invalid.
32372 * @param {Roo.form.Field} this
32373 * @param {String} msg The validation message
32378 * Fires after the field has been validated with no errors.
32379 * @param {Roo.form.Field} this
32385 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32388 labelAlign : 'top',
32390 dayAllowBlank : false,
32391 monthAllowBlank : false,
32392 yearAllowBlank : false,
32393 dayPlaceholder : '',
32394 monthPlaceholder : '',
32395 yearPlaceholder : '',
32399 isFormField : true,
32405 getAutoCreate : function()
32409 cls : 'row roo-date-split-field-group',
32414 cls : 'form-hidden-field roo-date-split-field-group-value',
32420 var labelCls = 'col-md-12';
32421 var contentCls = 'col-md-4';
32423 if(this.fieldLabel){
32427 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32431 html : this.fieldLabel
32436 if(this.labelAlign == 'left'){
32438 if(this.labelWidth > 12){
32439 label.style = "width: " + this.labelWidth + 'px';
32442 if(this.labelWidth < 13 && this.labelmd == 0){
32443 this.labelmd = this.labelWidth;
32446 if(this.labellg > 0){
32447 labelCls = ' col-lg-' + this.labellg;
32448 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32451 if(this.labelmd > 0){
32452 labelCls = ' col-md-' + this.labelmd;
32453 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32456 if(this.labelsm > 0){
32457 labelCls = ' col-sm-' + this.labelsm;
32458 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32461 if(this.labelxs > 0){
32462 labelCls = ' col-xs-' + this.labelxs;
32463 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32467 label.cls += ' ' + labelCls;
32469 cfg.cn.push(label);
32472 Roo.each(['day', 'month', 'year'], function(t){
32475 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32482 inputEl: function ()
32484 return this.el.select('.roo-date-split-field-group-value', true).first();
32487 onRender : function(ct, position)
32491 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32493 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32495 this.dayField = new Roo.bootstrap.ComboBox({
32496 allowBlank : this.dayAllowBlank,
32497 alwaysQuery : true,
32498 displayField : 'value',
32501 forceSelection : true,
32503 placeholder : this.dayPlaceholder,
32504 selectOnFocus : true,
32505 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32506 triggerAction : 'all',
32508 valueField : 'value',
32509 store : new Roo.data.SimpleStore({
32510 data : (function() {
32512 _this.fireEvent('days', _this, days);
32515 fields : [ 'value' ]
32518 select : function (_self, record, index)
32520 _this.setValue(_this.getValue());
32525 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32527 this.monthField = new Roo.bootstrap.MonthField({
32528 after : '<i class=\"fa fa-calendar\"></i>',
32529 allowBlank : this.monthAllowBlank,
32530 placeholder : this.monthPlaceholder,
32533 render : function (_self)
32535 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32536 e.preventDefault();
32540 select : function (_self, oldvalue, newvalue)
32542 _this.setValue(_this.getValue());
32547 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32549 this.yearField = new Roo.bootstrap.ComboBox({
32550 allowBlank : this.yearAllowBlank,
32551 alwaysQuery : true,
32552 displayField : 'value',
32555 forceSelection : true,
32557 placeholder : this.yearPlaceholder,
32558 selectOnFocus : true,
32559 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32560 triggerAction : 'all',
32562 valueField : 'value',
32563 store : new Roo.data.SimpleStore({
32564 data : (function() {
32566 _this.fireEvent('years', _this, years);
32569 fields : [ 'value' ]
32572 select : function (_self, record, index)
32574 _this.setValue(_this.getValue());
32579 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32582 setValue : function(v, format)
32584 this.inputEl.dom.value = v;
32586 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32588 var d = Date.parseDate(v, f);
32595 this.setDay(d.format(this.dayFormat));
32596 this.setMonth(d.format(this.monthFormat));
32597 this.setYear(d.format(this.yearFormat));
32604 setDay : function(v)
32606 this.dayField.setValue(v);
32607 this.inputEl.dom.value = this.getValue();
32612 setMonth : function(v)
32614 this.monthField.setValue(v, true);
32615 this.inputEl.dom.value = this.getValue();
32620 setYear : function(v)
32622 this.yearField.setValue(v);
32623 this.inputEl.dom.value = this.getValue();
32628 getDay : function()
32630 return this.dayField.getValue();
32633 getMonth : function()
32635 return this.monthField.getValue();
32638 getYear : function()
32640 return this.yearField.getValue();
32643 getValue : function()
32645 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32647 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32657 this.inputEl.dom.value = '';
32662 validate : function()
32664 var d = this.dayField.validate();
32665 var m = this.monthField.validate();
32666 var y = this.yearField.validate();
32671 (!this.dayAllowBlank && !d) ||
32672 (!this.monthAllowBlank && !m) ||
32673 (!this.yearAllowBlank && !y)
32678 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32687 this.markInvalid();
32692 markValid : function()
32695 var label = this.el.select('label', true).first();
32696 var icon = this.el.select('i.fa-star', true).first();
32702 this.fireEvent('valid', this);
32706 * Mark this field as invalid
32707 * @param {String} msg The validation message
32709 markInvalid : function(msg)
32712 var label = this.el.select('label', true).first();
32713 var icon = this.el.select('i.fa-star', true).first();
32715 if(label && !icon){
32716 this.el.select('.roo-date-split-field-label', true).createChild({
32718 cls : 'text-danger fa fa-lg fa-star',
32719 tooltip : 'This field is required',
32720 style : 'margin-right:5px;'
32724 this.fireEvent('invalid', this, msg);
32727 clearInvalid : function()
32729 var label = this.el.select('label', true).first();
32730 var icon = this.el.select('i.fa-star', true).first();
32736 this.fireEvent('valid', this);
32739 getName: function()
32749 * http://masonry.desandro.com
32751 * The idea is to render all the bricks based on vertical width...
32753 * The original code extends 'outlayer' - we might need to use that....
32759 * @class Roo.bootstrap.LayoutMasonry
32760 * @extends Roo.bootstrap.Component
32761 * Bootstrap Layout Masonry class
32764 * Create a new Element
32765 * @param {Object} config The config object
32768 Roo.bootstrap.LayoutMasonry = function(config){
32770 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32774 Roo.bootstrap.LayoutMasonry.register(this);
32780 * Fire after layout the items
32781 * @param {Roo.bootstrap.LayoutMasonry} this
32782 * @param {Roo.EventObject} e
32789 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32792 * @cfg {Boolean} isLayoutInstant = no animation?
32794 isLayoutInstant : false, // needed?
32797 * @cfg {Number} boxWidth width of the columns
32802 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32807 * @cfg {Number} padWidth padding below box..
32812 * @cfg {Number} gutter gutter width..
32817 * @cfg {Number} maxCols maximum number of columns
32823 * @cfg {Boolean} isAutoInitial defalut true
32825 isAutoInitial : true,
32830 * @cfg {Boolean} isHorizontal defalut false
32832 isHorizontal : false,
32834 currentSize : null,
32840 bricks: null, //CompositeElement
32844 _isLayoutInited : false,
32846 // isAlternative : false, // only use for vertical layout...
32849 * @cfg {Number} alternativePadWidth padding below box..
32851 alternativePadWidth : 50,
32853 selectedBrick : [],
32855 getAutoCreate : function(){
32857 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32861 cls: 'blog-masonary-wrapper ' + this.cls,
32863 cls : 'mas-boxes masonary'
32870 getChildContainer: function( )
32872 if (this.boxesEl) {
32873 return this.boxesEl;
32876 this.boxesEl = this.el.select('.mas-boxes').first();
32878 return this.boxesEl;
32882 initEvents : function()
32886 if(this.isAutoInitial){
32887 Roo.log('hook children rendered');
32888 this.on('childrenrendered', function() {
32889 Roo.log('children rendered');
32895 initial : function()
32897 this.selectedBrick = [];
32899 this.currentSize = this.el.getBox(true);
32901 Roo.EventManager.onWindowResize(this.resize, this);
32903 if(!this.isAutoInitial){
32911 //this.layout.defer(500,this);
32915 resize : function()
32917 var cs = this.el.getBox(true);
32920 this.currentSize.width == cs.width &&
32921 this.currentSize.x == cs.x &&
32922 this.currentSize.height == cs.height &&
32923 this.currentSize.y == cs.y
32925 Roo.log("no change in with or X or Y");
32929 this.currentSize = cs;
32935 layout : function()
32937 this._resetLayout();
32939 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32941 this.layoutItems( isInstant );
32943 this._isLayoutInited = true;
32945 this.fireEvent('layout', this);
32949 _resetLayout : function()
32951 if(this.isHorizontal){
32952 this.horizontalMeasureColumns();
32956 this.verticalMeasureColumns();
32960 verticalMeasureColumns : function()
32962 this.getContainerWidth();
32964 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32965 // this.colWidth = Math.floor(this.containerWidth * 0.8);
32969 var boxWidth = this.boxWidth + this.padWidth;
32971 if(this.containerWidth < this.boxWidth){
32972 boxWidth = this.containerWidth
32975 var containerWidth = this.containerWidth;
32977 var cols = Math.floor(containerWidth / boxWidth);
32979 this.cols = Math.max( cols, 1 );
32981 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32983 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32985 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32987 this.colWidth = boxWidth + avail - this.padWidth;
32989 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32990 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
32993 horizontalMeasureColumns : function()
32995 this.getContainerWidth();
32997 var boxWidth = this.boxWidth;
32999 if(this.containerWidth < boxWidth){
33000 boxWidth = this.containerWidth;
33003 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33005 this.el.setHeight(boxWidth);
33009 getContainerWidth : function()
33011 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33014 layoutItems : function( isInstant )
33016 Roo.log(this.bricks);
33018 var items = Roo.apply([], this.bricks);
33020 if(this.isHorizontal){
33021 this._horizontalLayoutItems( items , isInstant );
33025 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33026 // this._verticalAlternativeLayoutItems( items , isInstant );
33030 this._verticalLayoutItems( items , isInstant );
33034 _verticalLayoutItems : function ( items , isInstant)
33036 if ( !items || !items.length ) {
33041 ['xs', 'xs', 'xs', 'tall'],
33042 ['xs', 'xs', 'tall'],
33043 ['xs', 'xs', 'sm'],
33044 ['xs', 'xs', 'xs'],
33050 ['sm', 'xs', 'xs'],
33054 ['tall', 'xs', 'xs', 'xs'],
33055 ['tall', 'xs', 'xs'],
33067 Roo.each(items, function(item, k){
33069 switch (item.size) {
33070 // these layouts take up a full box,
33081 boxes.push([item]);
33104 var filterPattern = function(box, length)
33112 var pattern = box.slice(0, length);
33116 Roo.each(pattern, function(i){
33117 format.push(i.size);
33120 Roo.each(standard, function(s){
33122 if(String(s) != String(format)){
33131 if(!match && length == 1){
33136 filterPattern(box, length - 1);
33140 queue.push(pattern);
33142 box = box.slice(length, box.length);
33144 filterPattern(box, 4);
33150 Roo.each(boxes, function(box, k){
33156 if(box.length == 1){
33161 filterPattern(box, 4);
33165 this._processVerticalLayoutQueue( queue, isInstant );
33169 // _verticalAlternativeLayoutItems : function( items , isInstant )
33171 // if ( !items || !items.length ) {
33175 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33179 _horizontalLayoutItems : function ( items , isInstant)
33181 if ( !items || !items.length || items.length < 3) {
33187 var eItems = items.slice(0, 3);
33189 items = items.slice(3, items.length);
33192 ['xs', 'xs', 'xs', 'wide'],
33193 ['xs', 'xs', 'wide'],
33194 ['xs', 'xs', 'sm'],
33195 ['xs', 'xs', 'xs'],
33201 ['sm', 'xs', 'xs'],
33205 ['wide', 'xs', 'xs', 'xs'],
33206 ['wide', 'xs', 'xs'],
33219 Roo.each(items, function(item, k){
33221 switch (item.size) {
33232 boxes.push([item]);
33256 var filterPattern = function(box, length)
33264 var pattern = box.slice(0, length);
33268 Roo.each(pattern, function(i){
33269 format.push(i.size);
33272 Roo.each(standard, function(s){
33274 if(String(s) != String(format)){
33283 if(!match && length == 1){
33288 filterPattern(box, length - 1);
33292 queue.push(pattern);
33294 box = box.slice(length, box.length);
33296 filterPattern(box, 4);
33302 Roo.each(boxes, function(box, k){
33308 if(box.length == 1){
33313 filterPattern(box, 4);
33320 var pos = this.el.getBox(true);
33324 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33326 var hit_end = false;
33328 Roo.each(queue, function(box){
33332 Roo.each(box, function(b){
33334 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33344 Roo.each(box, function(b){
33346 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33349 mx = Math.max(mx, b.x);
33353 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33357 Roo.each(box, function(b){
33359 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33373 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33376 /** Sets position of item in DOM
33377 * @param {Element} item
33378 * @param {Number} x - horizontal position
33379 * @param {Number} y - vertical position
33380 * @param {Boolean} isInstant - disables transitions
33382 _processVerticalLayoutQueue : function( queue, isInstant )
33384 var pos = this.el.getBox(true);
33389 for (var i = 0; i < this.cols; i++){
33393 Roo.each(queue, function(box, k){
33395 var col = k % this.cols;
33397 Roo.each(box, function(b,kk){
33399 b.el.position('absolute');
33401 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33402 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33404 if(b.size == 'md-left' || b.size == 'md-right'){
33405 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33406 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33409 b.el.setWidth(width);
33410 b.el.setHeight(height);
33412 b.el.select('iframe',true).setSize(width,height);
33416 for (var i = 0; i < this.cols; i++){
33418 if(maxY[i] < maxY[col]){
33423 col = Math.min(col, i);
33427 x = pos.x + col * (this.colWidth + this.padWidth);
33431 var positions = [];
33433 switch (box.length){
33435 positions = this.getVerticalOneBoxColPositions(x, y, box);
33438 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33441 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33444 positions = this.getVerticalFourBoxColPositions(x, y, box);
33450 Roo.each(box, function(b,kk){
33452 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33454 var sz = b.el.getSize();
33456 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33464 for (var i = 0; i < this.cols; i++){
33465 mY = Math.max(mY, maxY[i]);
33468 this.el.setHeight(mY - pos.y);
33472 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33474 // var pos = this.el.getBox(true);
33477 // var maxX = pos.right;
33479 // var maxHeight = 0;
33481 // Roo.each(items, function(item, k){
33485 // item.el.position('absolute');
33487 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33489 // item.el.setWidth(width);
33491 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33493 // item.el.setHeight(height);
33496 // item.el.setXY([x, y], isInstant ? false : true);
33498 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33501 // y = y + height + this.alternativePadWidth;
33503 // maxHeight = maxHeight + height + this.alternativePadWidth;
33507 // this.el.setHeight(maxHeight);
33511 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33513 var pos = this.el.getBox(true);
33518 var maxX = pos.right;
33520 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33522 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33524 Roo.each(queue, function(box, k){
33526 Roo.each(box, function(b, kk){
33528 b.el.position('absolute');
33530 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33531 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33533 if(b.size == 'md-left' || b.size == 'md-right'){
33534 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33535 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33538 b.el.setWidth(width);
33539 b.el.setHeight(height);
33547 var positions = [];
33549 switch (box.length){
33551 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33554 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33557 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33560 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33566 Roo.each(box, function(b,kk){
33568 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33570 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33578 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33580 Roo.each(eItems, function(b,k){
33582 b.size = (k == 0) ? 'sm' : 'xs';
33583 b.x = (k == 0) ? 2 : 1;
33584 b.y = (k == 0) ? 2 : 1;
33586 b.el.position('absolute');
33588 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33590 b.el.setWidth(width);
33592 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33594 b.el.setHeight(height);
33598 var positions = [];
33601 x : maxX - this.unitWidth * 2 - this.gutter,
33606 x : maxX - this.unitWidth,
33607 y : minY + (this.unitWidth + this.gutter) * 2
33611 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33615 Roo.each(eItems, function(b,k){
33617 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33623 getVerticalOneBoxColPositions : function(x, y, box)
33627 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33629 if(box[0].size == 'md-left'){
33633 if(box[0].size == 'md-right'){
33638 x : x + (this.unitWidth + this.gutter) * rand,
33645 getVerticalTwoBoxColPositions : function(x, y, box)
33649 if(box[0].size == 'xs'){
33653 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33657 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33671 x : x + (this.unitWidth + this.gutter) * 2,
33672 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33679 getVerticalThreeBoxColPositions : function(x, y, box)
33683 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33691 x : x + (this.unitWidth + this.gutter) * 1,
33696 x : x + (this.unitWidth + this.gutter) * 2,
33704 if(box[0].size == 'xs' && box[1].size == 'xs'){
33713 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33717 x : x + (this.unitWidth + this.gutter) * 1,
33731 x : x + (this.unitWidth + this.gutter) * 2,
33736 x : x + (this.unitWidth + this.gutter) * 2,
33737 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33744 getVerticalFourBoxColPositions : function(x, y, box)
33748 if(box[0].size == 'xs'){
33757 y : y + (this.unitHeight + this.gutter) * 1
33762 y : y + (this.unitHeight + this.gutter) * 2
33766 x : x + (this.unitWidth + this.gutter) * 1,
33780 x : x + (this.unitWidth + this.gutter) * 2,
33785 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33786 y : y + (this.unitHeight + this.gutter) * 1
33790 x : x + (this.unitWidth + this.gutter) * 2,
33791 y : y + (this.unitWidth + this.gutter) * 2
33798 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33802 if(box[0].size == 'md-left'){
33804 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33811 if(box[0].size == 'md-right'){
33813 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33814 y : minY + (this.unitWidth + this.gutter) * 1
33820 var rand = Math.floor(Math.random() * (4 - box[0].y));
33823 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33824 y : minY + (this.unitWidth + this.gutter) * rand
33831 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33835 if(box[0].size == 'xs'){
33838 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33843 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33844 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33852 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33857 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33858 y : minY + (this.unitWidth + this.gutter) * 2
33865 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33869 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33872 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33877 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33878 y : minY + (this.unitWidth + this.gutter) * 1
33882 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33883 y : minY + (this.unitWidth + this.gutter) * 2
33890 if(box[0].size == 'xs' && box[1].size == 'xs'){
33893 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33898 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33903 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33904 y : minY + (this.unitWidth + this.gutter) * 1
33912 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33917 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33918 y : minY + (this.unitWidth + this.gutter) * 2
33922 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33923 y : minY + (this.unitWidth + this.gutter) * 2
33930 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33934 if(box[0].size == 'xs'){
33937 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
33947 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),
33952 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33953 y : minY + (this.unitWidth + this.gutter) * 1
33961 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33966 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
33972 y : minY + (this.unitWidth + this.gutter) * 2
33976 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),
33977 y : minY + (this.unitWidth + this.gutter) * 2
33985 * remove a Masonry Brick
33986 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33988 removeBrick : function(brick_id)
33994 for (var i = 0; i<this.bricks.length; i++) {
33995 if (this.bricks[i].id == brick_id) {
33996 this.bricks.splice(i,1);
33997 this.el.dom.removeChild(Roo.get(brick_id).dom);
34004 * adds a Masonry Brick
34005 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34007 addBrick : function(cfg)
34009 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34010 //this.register(cn);
34011 cn.parentId = this.id;
34012 cn.render(this.el);
34017 * register a Masonry Brick
34018 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34021 register : function(brick)
34023 this.bricks.push(brick);
34024 brick.masonryId = this.id;
34028 * clear all the Masonry Brick
34030 clearAll : function()
34033 //this.getChildContainer().dom.innerHTML = "";
34034 this.el.dom.innerHTML = '';
34037 getSelected : function()
34039 if (!this.selectedBrick) {
34043 return this.selectedBrick;
34047 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34051 * register a Masonry Layout
34052 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34055 register : function(layout)
34057 this.groups[layout.id] = layout;
34060 * fetch a Masonry Layout based on the masonry layout ID
34061 * @param {string} the masonry layout to add
34062 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34065 get: function(layout_id) {
34066 if (typeof(this.groups[layout_id]) == 'undefined') {
34069 return this.groups[layout_id] ;
34081 * http://masonry.desandro.com
34083 * The idea is to render all the bricks based on vertical width...
34085 * The original code extends 'outlayer' - we might need to use that....
34091 * @class Roo.bootstrap.LayoutMasonryAuto
34092 * @extends Roo.bootstrap.Component
34093 * Bootstrap Layout Masonry class
34096 * Create a new Element
34097 * @param {Object} config The config object
34100 Roo.bootstrap.LayoutMasonryAuto = function(config){
34101 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34104 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34107 * @cfg {Boolean} isFitWidth - resize the width..
34109 isFitWidth : false, // options..
34111 * @cfg {Boolean} isOriginLeft = left align?
34113 isOriginLeft : true,
34115 * @cfg {Boolean} isOriginTop = top align?
34117 isOriginTop : false,
34119 * @cfg {Boolean} isLayoutInstant = no animation?
34121 isLayoutInstant : false, // needed?
34123 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34125 isResizingContainer : true,
34127 * @cfg {Number} columnWidth width of the columns
34133 * @cfg {Number} maxCols maximum number of columns
34138 * @cfg {Number} padHeight padding below box..
34144 * @cfg {Boolean} isAutoInitial defalut true
34147 isAutoInitial : true,
34153 initialColumnWidth : 0,
34154 currentSize : null,
34156 colYs : null, // array.
34163 bricks: null, //CompositeElement
34164 cols : 0, // array?
34165 // element : null, // wrapped now this.el
34166 _isLayoutInited : null,
34169 getAutoCreate : function(){
34173 cls: 'blog-masonary-wrapper ' + this.cls,
34175 cls : 'mas-boxes masonary'
34182 getChildContainer: function( )
34184 if (this.boxesEl) {
34185 return this.boxesEl;
34188 this.boxesEl = this.el.select('.mas-boxes').first();
34190 return this.boxesEl;
34194 initEvents : function()
34198 if(this.isAutoInitial){
34199 Roo.log('hook children rendered');
34200 this.on('childrenrendered', function() {
34201 Roo.log('children rendered');
34208 initial : function()
34210 this.reloadItems();
34212 this.currentSize = this.el.getBox(true);
34214 /// was window resize... - let's see if this works..
34215 Roo.EventManager.onWindowResize(this.resize, this);
34217 if(!this.isAutoInitial){
34222 this.layout.defer(500,this);
34225 reloadItems: function()
34227 this.bricks = this.el.select('.masonry-brick', true);
34229 this.bricks.each(function(b) {
34230 //Roo.log(b.getSize());
34231 if (!b.attr('originalwidth')) {
34232 b.attr('originalwidth', b.getSize().width);
34237 Roo.log(this.bricks.elements.length);
34240 resize : function()
34243 var cs = this.el.getBox(true);
34245 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34246 Roo.log("no change in with or X");
34249 this.currentSize = cs;
34253 layout : function()
34256 this._resetLayout();
34257 //this._manageStamps();
34259 // don't animate first layout
34260 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34261 this.layoutItems( isInstant );
34263 // flag for initalized
34264 this._isLayoutInited = true;
34267 layoutItems : function( isInstant )
34269 //var items = this._getItemsForLayout( this.items );
34270 // original code supports filtering layout items.. we just ignore it..
34272 this._layoutItems( this.bricks , isInstant );
34274 this._postLayout();
34276 _layoutItems : function ( items , isInstant)
34278 //this.fireEvent( 'layout', this, items );
34281 if ( !items || !items.elements.length ) {
34282 // no items, emit event with empty array
34287 items.each(function(item) {
34288 Roo.log("layout item");
34290 // get x/y object from method
34291 var position = this._getItemLayoutPosition( item );
34293 position.item = item;
34294 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34295 queue.push( position );
34298 this._processLayoutQueue( queue );
34300 /** Sets position of item in DOM
34301 * @param {Element} item
34302 * @param {Number} x - horizontal position
34303 * @param {Number} y - vertical position
34304 * @param {Boolean} isInstant - disables transitions
34306 _processLayoutQueue : function( queue )
34308 for ( var i=0, len = queue.length; i < len; i++ ) {
34309 var obj = queue[i];
34310 obj.item.position('absolute');
34311 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34317 * Any logic you want to do after each layout,
34318 * i.e. size the container
34320 _postLayout : function()
34322 this.resizeContainer();
34325 resizeContainer : function()
34327 if ( !this.isResizingContainer ) {
34330 var size = this._getContainerSize();
34332 this.el.setSize(size.width,size.height);
34333 this.boxesEl.setSize(size.width,size.height);
34339 _resetLayout : function()
34341 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34342 this.colWidth = this.el.getWidth();
34343 //this.gutter = this.el.getWidth();
34345 this.measureColumns();
34351 this.colYs.push( 0 );
34357 measureColumns : function()
34359 this.getContainerWidth();
34360 // if columnWidth is 0, default to outerWidth of first item
34361 if ( !this.columnWidth ) {
34362 var firstItem = this.bricks.first();
34363 Roo.log(firstItem);
34364 this.columnWidth = this.containerWidth;
34365 if (firstItem && firstItem.attr('originalwidth') ) {
34366 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34368 // columnWidth fall back to item of first element
34369 Roo.log("set column width?");
34370 this.initialColumnWidth = this.columnWidth ;
34372 // if first elem has no width, default to size of container
34377 if (this.initialColumnWidth) {
34378 this.columnWidth = this.initialColumnWidth;
34383 // column width is fixed at the top - however if container width get's smaller we should
34386 // this bit calcs how man columns..
34388 var columnWidth = this.columnWidth += this.gutter;
34390 // calculate columns
34391 var containerWidth = this.containerWidth + this.gutter;
34393 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34394 // fix rounding errors, typically with gutters
34395 var excess = columnWidth - containerWidth % columnWidth;
34398 // if overshoot is less than a pixel, round up, otherwise floor it
34399 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34400 cols = Math[ mathMethod ]( cols );
34401 this.cols = Math.max( cols, 1 );
34402 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34404 // padding positioning..
34405 var totalColWidth = this.cols * this.columnWidth;
34406 var padavail = this.containerWidth - totalColWidth;
34407 // so for 2 columns - we need 3 'pads'
34409 var padNeeded = (1+this.cols) * this.padWidth;
34411 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34413 this.columnWidth += padExtra
34414 //this.padWidth = Math.floor(padavail / ( this.cols));
34416 // adjust colum width so that padding is fixed??
34418 // we have 3 columns ... total = width * 3
34419 // we have X left over... that should be used by
34421 //if (this.expandC) {
34429 getContainerWidth : function()
34431 /* // container is parent if fit width
34432 var container = this.isFitWidth ? this.element.parentNode : this.element;
34433 // check that this.size and size are there
34434 // IE8 triggers resize on body size change, so they might not be
34436 var size = getSize( container ); //FIXME
34437 this.containerWidth = size && size.innerWidth; //FIXME
34440 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34444 _getItemLayoutPosition : function( item ) // what is item?
34446 // we resize the item to our columnWidth..
34448 item.setWidth(this.columnWidth);
34449 item.autoBoxAdjust = false;
34451 var sz = item.getSize();
34453 // how many columns does this brick span
34454 var remainder = this.containerWidth % this.columnWidth;
34456 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34457 // round if off by 1 pixel, otherwise use ceil
34458 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34459 colSpan = Math.min( colSpan, this.cols );
34461 // normally this should be '1' as we dont' currently allow multi width columns..
34463 var colGroup = this._getColGroup( colSpan );
34464 // get the minimum Y value from the columns
34465 var minimumY = Math.min.apply( Math, colGroup );
34466 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34468 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34470 // position the brick
34472 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34473 y: this.currentSize.y + minimumY + this.padHeight
34477 // apply setHeight to necessary columns
34478 var setHeight = minimumY + sz.height + this.padHeight;
34479 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34481 var setSpan = this.cols + 1 - colGroup.length;
34482 for ( var i = 0; i < setSpan; i++ ) {
34483 this.colYs[ shortColIndex + i ] = setHeight ;
34490 * @param {Number} colSpan - number of columns the element spans
34491 * @returns {Array} colGroup
34493 _getColGroup : function( colSpan )
34495 if ( colSpan < 2 ) {
34496 // if brick spans only one column, use all the column Ys
34501 // how many different places could this brick fit horizontally
34502 var groupCount = this.cols + 1 - colSpan;
34503 // for each group potential horizontal position
34504 for ( var i = 0; i < groupCount; i++ ) {
34505 // make an array of colY values for that one group
34506 var groupColYs = this.colYs.slice( i, i + colSpan );
34507 // and get the max value of the array
34508 colGroup[i] = Math.max.apply( Math, groupColYs );
34513 _manageStamp : function( stamp )
34515 var stampSize = stamp.getSize();
34516 var offset = stamp.getBox();
34517 // get the columns that this stamp affects
34518 var firstX = this.isOriginLeft ? offset.x : offset.right;
34519 var lastX = firstX + stampSize.width;
34520 var firstCol = Math.floor( firstX / this.columnWidth );
34521 firstCol = Math.max( 0, firstCol );
34523 var lastCol = Math.floor( lastX / this.columnWidth );
34524 // lastCol should not go over if multiple of columnWidth #425
34525 lastCol -= lastX % this.columnWidth ? 0 : 1;
34526 lastCol = Math.min( this.cols - 1, lastCol );
34528 // set colYs to bottom of the stamp
34529 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34532 for ( var i = firstCol; i <= lastCol; i++ ) {
34533 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34538 _getContainerSize : function()
34540 this.maxY = Math.max.apply( Math, this.colYs );
34545 if ( this.isFitWidth ) {
34546 size.width = this._getContainerFitWidth();
34552 _getContainerFitWidth : function()
34554 var unusedCols = 0;
34555 // count unused columns
34558 if ( this.colYs[i] !== 0 ) {
34563 // fit container to columns that have been used
34564 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34567 needsResizeLayout : function()
34569 var previousWidth = this.containerWidth;
34570 this.getContainerWidth();
34571 return previousWidth !== this.containerWidth;
34586 * @class Roo.bootstrap.MasonryBrick
34587 * @extends Roo.bootstrap.Component
34588 * Bootstrap MasonryBrick class
34591 * Create a new MasonryBrick
34592 * @param {Object} config The config object
34595 Roo.bootstrap.MasonryBrick = function(config){
34597 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34599 Roo.bootstrap.MasonryBrick.register(this);
34605 * When a MasonryBrick is clcik
34606 * @param {Roo.bootstrap.MasonryBrick} this
34607 * @param {Roo.EventObject} e
34613 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34616 * @cfg {String} title
34620 * @cfg {String} html
34624 * @cfg {String} bgimage
34628 * @cfg {String} videourl
34632 * @cfg {String} cls
34636 * @cfg {String} href
34640 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34645 * @cfg {String} placetitle (center|bottom)
34650 * @cfg {Boolean} isFitContainer defalut true
34652 isFitContainer : true,
34655 * @cfg {Boolean} preventDefault defalut false
34657 preventDefault : false,
34660 * @cfg {Boolean} inverse defalut false
34662 maskInverse : false,
34664 getAutoCreate : function()
34666 if(!this.isFitContainer){
34667 return this.getSplitAutoCreate();
34670 var cls = 'masonry-brick masonry-brick-full';
34672 if(this.href.length){
34673 cls += ' masonry-brick-link';
34676 if(this.bgimage.length){
34677 cls += ' masonry-brick-image';
34680 if(this.maskInverse){
34681 cls += ' mask-inverse';
34684 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34685 cls += ' enable-mask';
34689 cls += ' masonry-' + this.size + '-brick';
34692 if(this.placetitle.length){
34694 switch (this.placetitle) {
34696 cls += ' masonry-center-title';
34699 cls += ' masonry-bottom-title';
34706 if(!this.html.length && !this.bgimage.length){
34707 cls += ' masonry-center-title';
34710 if(!this.html.length && this.bgimage.length){
34711 cls += ' masonry-bottom-title';
34716 cls += ' ' + this.cls;
34720 tag: (this.href.length) ? 'a' : 'div',
34725 cls: 'masonry-brick-mask'
34729 cls: 'masonry-brick-paragraph',
34735 if(this.href.length){
34736 cfg.href = this.href;
34739 var cn = cfg.cn[1].cn;
34741 if(this.title.length){
34744 cls: 'masonry-brick-title',
34749 if(this.html.length){
34752 cls: 'masonry-brick-text',
34757 if (!this.title.length && !this.html.length) {
34758 cfg.cn[1].cls += ' hide';
34761 if(this.bgimage.length){
34764 cls: 'masonry-brick-image-view',
34769 if(this.videourl.length){
34770 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34771 // youtube support only?
34774 cls: 'masonry-brick-image-view',
34777 allowfullscreen : true
34785 getSplitAutoCreate : function()
34787 var cls = 'masonry-brick masonry-brick-split';
34789 if(this.href.length){
34790 cls += ' masonry-brick-link';
34793 if(this.bgimage.length){
34794 cls += ' masonry-brick-image';
34798 cls += ' masonry-' + this.size + '-brick';
34801 switch (this.placetitle) {
34803 cls += ' masonry-center-title';
34806 cls += ' masonry-bottom-title';
34809 if(!this.bgimage.length){
34810 cls += ' masonry-center-title';
34813 if(this.bgimage.length){
34814 cls += ' masonry-bottom-title';
34820 cls += ' ' + this.cls;
34824 tag: (this.href.length) ? 'a' : 'div',
34829 cls: 'masonry-brick-split-head',
34833 cls: 'masonry-brick-paragraph',
34840 cls: 'masonry-brick-split-body',
34846 if(this.href.length){
34847 cfg.href = this.href;
34850 if(this.title.length){
34851 cfg.cn[0].cn[0].cn.push({
34853 cls: 'masonry-brick-title',
34858 if(this.html.length){
34859 cfg.cn[1].cn.push({
34861 cls: 'masonry-brick-text',
34866 if(this.bgimage.length){
34867 cfg.cn[0].cn.push({
34869 cls: 'masonry-brick-image-view',
34874 if(this.videourl.length){
34875 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34876 // youtube support only?
34877 cfg.cn[0].cn.cn.push({
34879 cls: 'masonry-brick-image-view',
34882 allowfullscreen : true
34889 initEvents: function()
34891 switch (this.size) {
34924 this.el.on('touchstart', this.onTouchStart, this);
34925 this.el.on('touchmove', this.onTouchMove, this);
34926 this.el.on('touchend', this.onTouchEnd, this);
34927 this.el.on('contextmenu', this.onContextMenu, this);
34929 this.el.on('mouseenter' ,this.enter, this);
34930 this.el.on('mouseleave', this.leave, this);
34931 this.el.on('click', this.onClick, this);
34934 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34935 this.parent().bricks.push(this);
34940 onClick: function(e, el)
34942 var time = this.endTimer - this.startTimer;
34943 // Roo.log(e.preventDefault());
34946 e.preventDefault();
34951 if(!this.preventDefault){
34955 e.preventDefault();
34957 if (this.activeClass != '') {
34958 this.selectBrick();
34961 this.fireEvent('click', this, e);
34964 enter: function(e, el)
34966 e.preventDefault();
34968 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34972 if(this.bgimage.length && this.html.length){
34973 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34977 leave: function(e, el)
34979 e.preventDefault();
34981 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34985 if(this.bgimage.length && this.html.length){
34986 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34990 onTouchStart: function(e, el)
34992 // e.preventDefault();
34994 this.touchmoved = false;
34996 if(!this.isFitContainer){
35000 if(!this.bgimage.length || !this.html.length){
35004 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35006 this.timer = new Date().getTime();
35010 onTouchMove: function(e, el)
35012 this.touchmoved = true;
35015 onContextMenu : function(e,el)
35017 e.preventDefault();
35018 e.stopPropagation();
35022 onTouchEnd: function(e, el)
35024 // e.preventDefault();
35026 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35033 if(!this.bgimage.length || !this.html.length){
35035 if(this.href.length){
35036 window.location.href = this.href;
35042 if(!this.isFitContainer){
35046 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35048 window.location.href = this.href;
35051 //selection on single brick only
35052 selectBrick : function() {
35054 if (!this.parentId) {
35058 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35059 var index = m.selectedBrick.indexOf(this.id);
35062 m.selectedBrick.splice(index,1);
35063 this.el.removeClass(this.activeClass);
35067 for(var i = 0; i < m.selectedBrick.length; i++) {
35068 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35069 b.el.removeClass(b.activeClass);
35072 m.selectedBrick = [];
35074 m.selectedBrick.push(this.id);
35075 this.el.addClass(this.activeClass);
35079 isSelected : function(){
35080 return this.el.hasClass(this.activeClass);
35085 Roo.apply(Roo.bootstrap.MasonryBrick, {
35088 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35090 * register a Masonry Brick
35091 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35094 register : function(brick)
35096 //this.groups[brick.id] = brick;
35097 this.groups.add(brick.id, brick);
35100 * fetch a masonry brick based on the masonry brick ID
35101 * @param {string} the masonry brick to add
35102 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35105 get: function(brick_id)
35107 // if (typeof(this.groups[brick_id]) == 'undefined') {
35110 // return this.groups[brick_id] ;
35112 if(this.groups.key(brick_id)) {
35113 return this.groups.key(brick_id);
35131 * @class Roo.bootstrap.Brick
35132 * @extends Roo.bootstrap.Component
35133 * Bootstrap Brick class
35136 * Create a new Brick
35137 * @param {Object} config The config object
35140 Roo.bootstrap.Brick = function(config){
35141 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35147 * When a Brick is click
35148 * @param {Roo.bootstrap.Brick} this
35149 * @param {Roo.EventObject} e
35155 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35158 * @cfg {String} title
35162 * @cfg {String} html
35166 * @cfg {String} bgimage
35170 * @cfg {String} cls
35174 * @cfg {String} href
35178 * @cfg {String} video
35182 * @cfg {Boolean} square
35186 getAutoCreate : function()
35188 var cls = 'roo-brick';
35190 if(this.href.length){
35191 cls += ' roo-brick-link';
35194 if(this.bgimage.length){
35195 cls += ' roo-brick-image';
35198 if(!this.html.length && !this.bgimage.length){
35199 cls += ' roo-brick-center-title';
35202 if(!this.html.length && this.bgimage.length){
35203 cls += ' roo-brick-bottom-title';
35207 cls += ' ' + this.cls;
35211 tag: (this.href.length) ? 'a' : 'div',
35216 cls: 'roo-brick-paragraph',
35222 if(this.href.length){
35223 cfg.href = this.href;
35226 var cn = cfg.cn[0].cn;
35228 if(this.title.length){
35231 cls: 'roo-brick-title',
35236 if(this.html.length){
35239 cls: 'roo-brick-text',
35246 if(this.bgimage.length){
35249 cls: 'roo-brick-image-view',
35257 initEvents: function()
35259 if(this.title.length || this.html.length){
35260 this.el.on('mouseenter' ,this.enter, this);
35261 this.el.on('mouseleave', this.leave, this);
35264 Roo.EventManager.onWindowResize(this.resize, this);
35266 if(this.bgimage.length){
35267 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35268 this.imageEl.on('load', this.onImageLoad, this);
35275 onImageLoad : function()
35280 resize : function()
35282 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35284 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35286 if(this.bgimage.length){
35287 var image = this.el.select('.roo-brick-image-view', true).first();
35289 image.setWidth(paragraph.getWidth());
35292 image.setHeight(paragraph.getWidth());
35295 this.el.setHeight(image.getHeight());
35296 paragraph.setHeight(image.getHeight());
35302 enter: function(e, el)
35304 e.preventDefault();
35306 if(this.bgimage.length){
35307 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35308 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35312 leave: function(e, el)
35314 e.preventDefault();
35316 if(this.bgimage.length){
35317 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35318 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35333 * @class Roo.bootstrap.NumberField
35334 * @extends Roo.bootstrap.Input
35335 * Bootstrap NumberField class
35341 * Create a new NumberField
35342 * @param {Object} config The config object
35345 Roo.bootstrap.NumberField = function(config){
35346 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35349 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35352 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35354 allowDecimals : true,
35356 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35358 decimalSeparator : ".",
35360 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35362 decimalPrecision : 2,
35364 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35366 allowNegative : true,
35369 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35373 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35375 minValue : Number.NEGATIVE_INFINITY,
35377 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35379 maxValue : Number.MAX_VALUE,
35381 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35383 minText : "The minimum value for this field is {0}",
35385 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35387 maxText : "The maximum value for this field is {0}",
35389 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35390 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35392 nanText : "{0} is not a valid number",
35394 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35396 thousandsDelimiter : false,
35398 * @cfg {String} valueAlign alignment of value
35400 valueAlign : "left",
35402 getAutoCreate : function()
35404 var hiddenInput = {
35408 cls: 'hidden-number-input'
35412 hiddenInput.name = this.name;
35417 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35419 this.name = hiddenInput.name;
35421 if(cfg.cn.length > 0) {
35422 cfg.cn.push(hiddenInput);
35429 initEvents : function()
35431 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35433 var allowed = "0123456789";
35435 if(this.allowDecimals){
35436 allowed += this.decimalSeparator;
35439 if(this.allowNegative){
35443 if(this.thousandsDelimiter) {
35447 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35449 var keyPress = function(e){
35451 var k = e.getKey();
35453 var c = e.getCharCode();
35456 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35457 allowed.indexOf(String.fromCharCode(c)) === -1
35463 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35467 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35472 this.el.on("keypress", keyPress, this);
35475 validateValue : function(value)
35478 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35482 var num = this.parseValue(value);
35485 this.markInvalid(String.format(this.nanText, value));
35489 if(num < this.minValue){
35490 this.markInvalid(String.format(this.minText, this.minValue));
35494 if(num > this.maxValue){
35495 this.markInvalid(String.format(this.maxText, this.maxValue));
35502 getValue : function()
35504 var v = this.hiddenEl().getValue();
35506 return this.fixPrecision(this.parseValue(v));
35509 parseValue : function(value)
35511 if(this.thousandsDelimiter) {
35513 r = new RegExp(",", "g");
35514 value = value.replace(r, "");
35517 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35518 return isNaN(value) ? '' : value;
35521 fixPrecision : function(value)
35523 if(this.thousandsDelimiter) {
35525 r = new RegExp(",", "g");
35526 value = value.replace(r, "");
35529 var nan = isNaN(value);
35531 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35532 return nan ? '' : value;
35534 return parseFloat(value).toFixed(this.decimalPrecision);
35537 setValue : function(v)
35539 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35545 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35547 this.inputEl().dom.value = (v == '') ? '' :
35548 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35550 if(!this.allowZero && v === '0') {
35551 this.hiddenEl().dom.value = '';
35552 this.inputEl().dom.value = '';
35559 decimalPrecisionFcn : function(v)
35561 return Math.floor(v);
35564 beforeBlur : function()
35566 var v = this.parseValue(this.getRawValue());
35568 if(v || v === 0 || v === ''){
35573 hiddenEl : function()
35575 return this.el.select('input.hidden-number-input',true).first();
35587 * @class Roo.bootstrap.DocumentSlider
35588 * @extends Roo.bootstrap.Component
35589 * Bootstrap DocumentSlider class
35592 * Create a new DocumentViewer
35593 * @param {Object} config The config object
35596 Roo.bootstrap.DocumentSlider = function(config){
35597 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35604 * Fire after initEvent
35605 * @param {Roo.bootstrap.DocumentSlider} this
35610 * Fire after update
35611 * @param {Roo.bootstrap.DocumentSlider} this
35617 * @param {Roo.bootstrap.DocumentSlider} this
35623 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35629 getAutoCreate : function()
35633 cls : 'roo-document-slider',
35637 cls : 'roo-document-slider-header',
35641 cls : 'roo-document-slider-header-title'
35647 cls : 'roo-document-slider-body',
35651 cls : 'roo-document-slider-prev',
35655 cls : 'fa fa-chevron-left'
35661 cls : 'roo-document-slider-thumb',
35665 cls : 'roo-document-slider-image'
35671 cls : 'roo-document-slider-next',
35675 cls : 'fa fa-chevron-right'
35687 initEvents : function()
35689 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35690 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35692 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35693 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35695 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35696 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35698 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35699 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35701 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35702 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35704 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35705 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35707 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35708 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35710 this.thumbEl.on('click', this.onClick, this);
35712 this.prevIndicator.on('click', this.prev, this);
35714 this.nextIndicator.on('click', this.next, this);
35718 initial : function()
35720 if(this.files.length){
35721 this.indicator = 1;
35725 this.fireEvent('initial', this);
35728 update : function()
35730 this.imageEl.attr('src', this.files[this.indicator - 1]);
35732 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35734 this.prevIndicator.show();
35736 if(this.indicator == 1){
35737 this.prevIndicator.hide();
35740 this.nextIndicator.show();
35742 if(this.indicator == this.files.length){
35743 this.nextIndicator.hide();
35746 this.thumbEl.scrollTo('top');
35748 this.fireEvent('update', this);
35751 onClick : function(e)
35753 e.preventDefault();
35755 this.fireEvent('click', this);
35760 e.preventDefault();
35762 this.indicator = Math.max(1, this.indicator - 1);
35769 e.preventDefault();
35771 this.indicator = Math.min(this.files.length, this.indicator + 1);
35785 * @class Roo.bootstrap.RadioSet
35786 * @extends Roo.bootstrap.Input
35787 * Bootstrap RadioSet class
35788 * @cfg {String} indicatorpos (left|right) default left
35789 * @cfg {Boolean} inline (true|false) inline the element (default true)
35790 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35792 * Create a new RadioSet
35793 * @param {Object} config The config object
35796 Roo.bootstrap.RadioSet = function(config){
35798 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35802 Roo.bootstrap.RadioSet.register(this);
35807 * Fires when the element is checked or unchecked.
35808 * @param {Roo.bootstrap.RadioSet} this This radio
35809 * @param {Roo.bootstrap.Radio} item The checked item
35814 * Fires when the element is click.
35815 * @param {Roo.bootstrap.RadioSet} this This radio set
35816 * @param {Roo.bootstrap.Radio} item The checked item
35817 * @param {Roo.EventObject} e The event object
35824 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35832 indicatorpos : 'left',
35834 getAutoCreate : function()
35838 cls : 'roo-radio-set-label',
35842 html : this.fieldLabel
35846 if (Roo.bootstrap.version == 3) {
35849 if(this.indicatorpos == 'left'){
35852 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35853 tooltip : 'This field is required'
35858 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35859 tooltip : 'This field is required'
35865 cls : 'roo-radio-set-items'
35868 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35870 if (align === 'left' && this.fieldLabel.length) {
35873 cls : "roo-radio-set-right",
35879 if(this.labelWidth > 12){
35880 label.style = "width: " + this.labelWidth + 'px';
35883 if(this.labelWidth < 13 && this.labelmd == 0){
35884 this.labelmd = this.labelWidth;
35887 if(this.labellg > 0){
35888 label.cls += ' col-lg-' + this.labellg;
35889 items.cls += ' col-lg-' + (12 - this.labellg);
35892 if(this.labelmd > 0){
35893 label.cls += ' col-md-' + this.labelmd;
35894 items.cls += ' col-md-' + (12 - this.labelmd);
35897 if(this.labelsm > 0){
35898 label.cls += ' col-sm-' + this.labelsm;
35899 items.cls += ' col-sm-' + (12 - this.labelsm);
35902 if(this.labelxs > 0){
35903 label.cls += ' col-xs-' + this.labelxs;
35904 items.cls += ' col-xs-' + (12 - this.labelxs);
35910 cls : 'roo-radio-set',
35914 cls : 'roo-radio-set-input',
35917 value : this.value ? this.value : ''
35924 if(this.weight.length){
35925 cfg.cls += ' roo-radio-' + this.weight;
35929 cfg.cls += ' roo-radio-set-inline';
35933 ['xs','sm','md','lg'].map(function(size){
35934 if (settings[size]) {
35935 cfg.cls += ' col-' + size + '-' + settings[size];
35943 initEvents : function()
35945 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35946 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35948 if(!this.fieldLabel.length){
35949 this.labelEl.hide();
35952 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35953 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35955 this.indicator = this.indicatorEl();
35957 if(this.indicator){
35958 this.indicator.addClass('invisible');
35961 this.originalValue = this.getValue();
35965 inputEl: function ()
35967 return this.el.select('.roo-radio-set-input', true).first();
35970 getChildContainer : function()
35972 return this.itemsEl;
35975 register : function(item)
35977 this.radioes.push(item);
35981 validate : function()
35983 if(this.getVisibilityEl().hasClass('hidden')){
35989 Roo.each(this.radioes, function(i){
35998 if(this.allowBlank) {
36002 if(this.disabled || valid){
36007 this.markInvalid();
36012 markValid : function()
36014 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36015 this.indicatorEl().removeClass('visible');
36016 this.indicatorEl().addClass('invisible');
36020 if (Roo.bootstrap.version == 3) {
36021 this.el.removeClass([this.invalidClass, this.validClass]);
36022 this.el.addClass(this.validClass);
36024 this.el.removeClass(['is-invalid','is-valid']);
36025 this.el.addClass(['is-valid']);
36027 this.fireEvent('valid', this);
36030 markInvalid : function(msg)
36032 if(this.allowBlank || this.disabled){
36036 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36037 this.indicatorEl().removeClass('invisible');
36038 this.indicatorEl().addClass('visible');
36040 if (Roo.bootstrap.version == 3) {
36041 this.el.removeClass([this.invalidClass, this.validClass]);
36042 this.el.addClass(this.invalidClass);
36044 this.el.removeClass(['is-invalid','is-valid']);
36045 this.el.addClass(['is-invalid']);
36048 this.fireEvent('invalid', this, msg);
36052 setValue : function(v, suppressEvent)
36054 if(this.value === v){
36061 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36064 Roo.each(this.radioes, function(i){
36066 i.el.removeClass('checked');
36069 Roo.each(this.radioes, function(i){
36071 if(i.value === v || i.value.toString() === v.toString()){
36073 i.el.addClass('checked');
36075 if(suppressEvent !== true){
36076 this.fireEvent('check', this, i);
36087 clearInvalid : function(){
36089 if(!this.el || this.preventMark){
36093 this.el.removeClass([this.invalidClass]);
36095 this.fireEvent('valid', this);
36100 Roo.apply(Roo.bootstrap.RadioSet, {
36104 register : function(set)
36106 this.groups[set.name] = set;
36109 get: function(name)
36111 if (typeof(this.groups[name]) == 'undefined') {
36115 return this.groups[name] ;
36121 * Ext JS Library 1.1.1
36122 * Copyright(c) 2006-2007, Ext JS, LLC.
36124 * Originally Released Under LGPL - original licence link has changed is not relivant.
36127 * <script type="text/javascript">
36132 * @class Roo.bootstrap.SplitBar
36133 * @extends Roo.util.Observable
36134 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36138 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36139 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36140 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36141 split.minSize = 100;
36142 split.maxSize = 600;
36143 split.animate = true;
36144 split.on('moved', splitterMoved);
36147 * Create a new SplitBar
36148 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36149 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36150 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36151 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36152 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36153 position of the SplitBar).
36155 Roo.bootstrap.SplitBar = function(cfg){
36160 // dragElement : elm
36161 // resizingElement: el,
36163 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36164 // placement : Roo.bootstrap.SplitBar.LEFT ,
36165 // existingProxy ???
36168 this.el = Roo.get(cfg.dragElement, true);
36169 this.el.dom.unselectable = "on";
36171 this.resizingEl = Roo.get(cfg.resizingElement, true);
36175 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36176 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36179 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36182 * The minimum size of the resizing element. (Defaults to 0)
36188 * The maximum size of the resizing element. (Defaults to 2000)
36191 this.maxSize = 2000;
36194 * Whether to animate the transition to the new size
36197 this.animate = false;
36200 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36203 this.useShim = false;
36208 if(!cfg.existingProxy){
36210 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36212 this.proxy = Roo.get(cfg.existingProxy).dom;
36215 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36218 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36221 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36224 this.dragSpecs = {};
36227 * @private The adapter to use to positon and resize elements
36229 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36230 this.adapter.init(this);
36232 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36234 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36235 this.el.addClass("roo-splitbar-h");
36238 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36239 this.el.addClass("roo-splitbar-v");
36245 * Fires when the splitter is moved (alias for {@link #event-moved})
36246 * @param {Roo.bootstrap.SplitBar} this
36247 * @param {Number} newSize the new width or height
36252 * Fires when the splitter is moved
36253 * @param {Roo.bootstrap.SplitBar} this
36254 * @param {Number} newSize the new width or height
36258 * @event beforeresize
36259 * Fires before the splitter is dragged
36260 * @param {Roo.bootstrap.SplitBar} this
36262 "beforeresize" : true,
36264 "beforeapply" : true
36267 Roo.util.Observable.call(this);
36270 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36271 onStartProxyDrag : function(x, y){
36272 this.fireEvent("beforeresize", this);
36274 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36276 o.enableDisplayMode("block");
36277 // all splitbars share the same overlay
36278 Roo.bootstrap.SplitBar.prototype.overlay = o;
36280 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36281 this.overlay.show();
36282 Roo.get(this.proxy).setDisplayed("block");
36283 var size = this.adapter.getElementSize(this);
36284 this.activeMinSize = this.getMinimumSize();;
36285 this.activeMaxSize = this.getMaximumSize();;
36286 var c1 = size - this.activeMinSize;
36287 var c2 = Math.max(this.activeMaxSize - size, 0);
36288 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36289 this.dd.resetConstraints();
36290 this.dd.setXConstraint(
36291 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36292 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36294 this.dd.setYConstraint(0, 0);
36296 this.dd.resetConstraints();
36297 this.dd.setXConstraint(0, 0);
36298 this.dd.setYConstraint(
36299 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36300 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36303 this.dragSpecs.startSize = size;
36304 this.dragSpecs.startPoint = [x, y];
36305 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36309 * @private Called after the drag operation by the DDProxy
36311 onEndProxyDrag : function(e){
36312 Roo.get(this.proxy).setDisplayed(false);
36313 var endPoint = Roo.lib.Event.getXY(e);
36315 this.overlay.hide();
36318 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36319 newSize = this.dragSpecs.startSize +
36320 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36321 endPoint[0] - this.dragSpecs.startPoint[0] :
36322 this.dragSpecs.startPoint[0] - endPoint[0]
36325 newSize = this.dragSpecs.startSize +
36326 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36327 endPoint[1] - this.dragSpecs.startPoint[1] :
36328 this.dragSpecs.startPoint[1] - endPoint[1]
36331 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36332 if(newSize != this.dragSpecs.startSize){
36333 if(this.fireEvent('beforeapply', this, newSize) !== false){
36334 this.adapter.setElementSize(this, newSize);
36335 this.fireEvent("moved", this, newSize);
36336 this.fireEvent("resize", this, newSize);
36342 * Get the adapter this SplitBar uses
36343 * @return The adapter object
36345 getAdapter : function(){
36346 return this.adapter;
36350 * Set the adapter this SplitBar uses
36351 * @param {Object} adapter A SplitBar adapter object
36353 setAdapter : function(adapter){
36354 this.adapter = adapter;
36355 this.adapter.init(this);
36359 * Gets the minimum size for the resizing element
36360 * @return {Number} The minimum size
36362 getMinimumSize : function(){
36363 return this.minSize;
36367 * Sets the minimum size for the resizing element
36368 * @param {Number} minSize The minimum size
36370 setMinimumSize : function(minSize){
36371 this.minSize = minSize;
36375 * Gets the maximum size for the resizing element
36376 * @return {Number} The maximum size
36378 getMaximumSize : function(){
36379 return this.maxSize;
36383 * Sets the maximum size for the resizing element
36384 * @param {Number} maxSize The maximum size
36386 setMaximumSize : function(maxSize){
36387 this.maxSize = maxSize;
36391 * Sets the initialize size for the resizing element
36392 * @param {Number} size The initial size
36394 setCurrentSize : function(size){
36395 var oldAnimate = this.animate;
36396 this.animate = false;
36397 this.adapter.setElementSize(this, size);
36398 this.animate = oldAnimate;
36402 * Destroy this splitbar.
36403 * @param {Boolean} removeEl True to remove the element
36405 destroy : function(removeEl){
36407 this.shim.remove();
36410 this.proxy.parentNode.removeChild(this.proxy);
36418 * @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.
36420 Roo.bootstrap.SplitBar.createProxy = function(dir){
36421 var proxy = new Roo.Element(document.createElement("div"));
36422 proxy.unselectable();
36423 var cls = 'roo-splitbar-proxy';
36424 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36425 document.body.appendChild(proxy.dom);
36430 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36431 * Default Adapter. It assumes the splitter and resizing element are not positioned
36432 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36434 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36437 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36438 // do nothing for now
36439 init : function(s){
36443 * Called before drag operations to get the current size of the resizing element.
36444 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36446 getElementSize : function(s){
36447 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36448 return s.resizingEl.getWidth();
36450 return s.resizingEl.getHeight();
36455 * Called after drag operations to set the size of the resizing element.
36456 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36457 * @param {Number} newSize The new size to set
36458 * @param {Function} onComplete A function to be invoked when resizing is complete
36460 setElementSize : function(s, newSize, onComplete){
36461 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36463 s.resizingEl.setWidth(newSize);
36465 onComplete(s, newSize);
36468 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36473 s.resizingEl.setHeight(newSize);
36475 onComplete(s, newSize);
36478 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36485 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36486 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36487 * Adapter that moves the splitter element to align with the resized sizing element.
36488 * Used with an absolute positioned SplitBar.
36489 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36490 * document.body, make sure you assign an id to the body element.
36492 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36493 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36494 this.container = Roo.get(container);
36497 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36498 init : function(s){
36499 this.basic.init(s);
36502 getElementSize : function(s){
36503 return this.basic.getElementSize(s);
36506 setElementSize : function(s, newSize, onComplete){
36507 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36510 moveSplitter : function(s){
36511 var yes = Roo.bootstrap.SplitBar;
36512 switch(s.placement){
36514 s.el.setX(s.resizingEl.getRight());
36517 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36520 s.el.setY(s.resizingEl.getBottom());
36523 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36530 * Orientation constant - Create a vertical SplitBar
36534 Roo.bootstrap.SplitBar.VERTICAL = 1;
36537 * Orientation constant - Create a horizontal SplitBar
36541 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36544 * Placement constant - The resizing element is to the left of the splitter element
36548 Roo.bootstrap.SplitBar.LEFT = 1;
36551 * Placement constant - The resizing element is to the right of the splitter element
36555 Roo.bootstrap.SplitBar.RIGHT = 2;
36558 * Placement constant - The resizing element is positioned above the splitter element
36562 Roo.bootstrap.SplitBar.TOP = 3;
36565 * Placement constant - The resizing element is positioned under splitter element
36569 Roo.bootstrap.SplitBar.BOTTOM = 4;
36570 Roo.namespace("Roo.bootstrap.layout");/*
36572 * Ext JS Library 1.1.1
36573 * Copyright(c) 2006-2007, Ext JS, LLC.
36575 * Originally Released Under LGPL - original licence link has changed is not relivant.
36578 * <script type="text/javascript">
36582 * @class Roo.bootstrap.layout.Manager
36583 * @extends Roo.bootstrap.Component
36584 * Base class for layout managers.
36586 Roo.bootstrap.layout.Manager = function(config)
36588 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36594 /** false to disable window resize monitoring @type Boolean */
36595 this.monitorWindowResize = true;
36600 * Fires when a layout is performed.
36601 * @param {Roo.LayoutManager} this
36605 * @event regionresized
36606 * Fires when the user resizes a region.
36607 * @param {Roo.LayoutRegion} region The resized region
36608 * @param {Number} newSize The new size (width for east/west, height for north/south)
36610 "regionresized" : true,
36612 * @event regioncollapsed
36613 * Fires when a region is collapsed.
36614 * @param {Roo.LayoutRegion} region The collapsed region
36616 "regioncollapsed" : true,
36618 * @event regionexpanded
36619 * Fires when a region is expanded.
36620 * @param {Roo.LayoutRegion} region The expanded region
36622 "regionexpanded" : true
36624 this.updating = false;
36627 this.el = Roo.get(config.el);
36633 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36638 monitorWindowResize : true,
36644 onRender : function(ct, position)
36647 this.el = Roo.get(ct);
36650 //this.fireEvent('render',this);
36654 initEvents: function()
36658 // ie scrollbar fix
36659 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36660 document.body.scroll = "no";
36661 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36662 this.el.position('relative');
36664 this.id = this.el.id;
36665 this.el.addClass("roo-layout-container");
36666 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36667 if(this.el.dom != document.body ) {
36668 this.el.on('resize', this.layout,this);
36669 this.el.on('show', this.layout,this);
36675 * Returns true if this layout is currently being updated
36676 * @return {Boolean}
36678 isUpdating : function(){
36679 return this.updating;
36683 * Suspend the LayoutManager from doing auto-layouts while
36684 * making multiple add or remove calls
36686 beginUpdate : function(){
36687 this.updating = true;
36691 * Restore auto-layouts and optionally disable the manager from performing a layout
36692 * @param {Boolean} noLayout true to disable a layout update
36694 endUpdate : function(noLayout){
36695 this.updating = false;
36701 layout: function(){
36705 onRegionResized : function(region, newSize){
36706 this.fireEvent("regionresized", region, newSize);
36710 onRegionCollapsed : function(region){
36711 this.fireEvent("regioncollapsed", region);
36714 onRegionExpanded : function(region){
36715 this.fireEvent("regionexpanded", region);
36719 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36720 * performs box-model adjustments.
36721 * @return {Object} The size as an object {width: (the width), height: (the height)}
36723 getViewSize : function()
36726 if(this.el.dom != document.body){
36727 size = this.el.getSize();
36729 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36731 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36732 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36737 * Returns the Element this layout is bound to.
36738 * @return {Roo.Element}
36740 getEl : function(){
36745 * Returns the specified region.
36746 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36747 * @return {Roo.LayoutRegion}
36749 getRegion : function(target){
36750 return this.regions[target.toLowerCase()];
36753 onWindowResize : function(){
36754 if(this.monitorWindowResize){
36761 * Ext JS Library 1.1.1
36762 * Copyright(c) 2006-2007, Ext JS, LLC.
36764 * Originally Released Under LGPL - original licence link has changed is not relivant.
36767 * <script type="text/javascript">
36770 * @class Roo.bootstrap.layout.Border
36771 * @extends Roo.bootstrap.layout.Manager
36772 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36773 * please see: examples/bootstrap/nested.html<br><br>
36775 <b>The container the layout is rendered into can be either the body element or any other element.
36776 If it is not the body element, the container needs to either be an absolute positioned element,
36777 or you will need to add "position:relative" to the css of the container. You will also need to specify
36778 the container size if it is not the body element.</b>
36781 * Create a new Border
36782 * @param {Object} config Configuration options
36784 Roo.bootstrap.layout.Border = function(config){
36785 config = config || {};
36786 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36790 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36791 if(config[region]){
36792 config[region].region = region;
36793 this.addRegion(config[region]);
36799 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36801 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36803 parent : false, // this might point to a 'nest' or a ???
36806 * Creates and adds a new region if it doesn't already exist.
36807 * @param {String} target The target region key (north, south, east, west or center).
36808 * @param {Object} config The regions config object
36809 * @return {BorderLayoutRegion} The new region
36811 addRegion : function(config)
36813 if(!this.regions[config.region]){
36814 var r = this.factory(config);
36815 this.bindRegion(r);
36817 return this.regions[config.region];
36821 bindRegion : function(r){
36822 this.regions[r.config.region] = r;
36824 r.on("visibilitychange", this.layout, this);
36825 r.on("paneladded", this.layout, this);
36826 r.on("panelremoved", this.layout, this);
36827 r.on("invalidated", this.layout, this);
36828 r.on("resized", this.onRegionResized, this);
36829 r.on("collapsed", this.onRegionCollapsed, this);
36830 r.on("expanded", this.onRegionExpanded, this);
36834 * Performs a layout update.
36836 layout : function()
36838 if(this.updating) {
36842 // render all the rebions if they have not been done alreayd?
36843 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36844 if(this.regions[region] && !this.regions[region].bodyEl){
36845 this.regions[region].onRender(this.el)
36849 var size = this.getViewSize();
36850 var w = size.width;
36851 var h = size.height;
36856 //var x = 0, y = 0;
36858 var rs = this.regions;
36859 var north = rs["north"];
36860 var south = rs["south"];
36861 var west = rs["west"];
36862 var east = rs["east"];
36863 var center = rs["center"];
36864 //if(this.hideOnLayout){ // not supported anymore
36865 //c.el.setStyle("display", "none");
36867 if(north && north.isVisible()){
36868 var b = north.getBox();
36869 var m = north.getMargins();
36870 b.width = w - (m.left+m.right);
36873 centerY = b.height + b.y + m.bottom;
36874 centerH -= centerY;
36875 north.updateBox(this.safeBox(b));
36877 if(south && south.isVisible()){
36878 var b = south.getBox();
36879 var m = south.getMargins();
36880 b.width = w - (m.left+m.right);
36882 var totalHeight = (b.height + m.top + m.bottom);
36883 b.y = h - totalHeight + m.top;
36884 centerH -= totalHeight;
36885 south.updateBox(this.safeBox(b));
36887 if(west && west.isVisible()){
36888 var b = west.getBox();
36889 var m = west.getMargins();
36890 b.height = centerH - (m.top+m.bottom);
36892 b.y = centerY + m.top;
36893 var totalWidth = (b.width + m.left + m.right);
36894 centerX += totalWidth;
36895 centerW -= totalWidth;
36896 west.updateBox(this.safeBox(b));
36898 if(east && east.isVisible()){
36899 var b = east.getBox();
36900 var m = east.getMargins();
36901 b.height = centerH - (m.top+m.bottom);
36902 var totalWidth = (b.width + m.left + m.right);
36903 b.x = w - totalWidth + m.left;
36904 b.y = centerY + m.top;
36905 centerW -= totalWidth;
36906 east.updateBox(this.safeBox(b));
36909 var m = center.getMargins();
36911 x: centerX + m.left,
36912 y: centerY + m.top,
36913 width: centerW - (m.left+m.right),
36914 height: centerH - (m.top+m.bottom)
36916 //if(this.hideOnLayout){
36917 //center.el.setStyle("display", "block");
36919 center.updateBox(this.safeBox(centerBox));
36922 this.fireEvent("layout", this);
36926 safeBox : function(box){
36927 box.width = Math.max(0, box.width);
36928 box.height = Math.max(0, box.height);
36933 * Adds a ContentPanel (or subclass) to this layout.
36934 * @param {String} target The target region key (north, south, east, west or center).
36935 * @param {Roo.ContentPanel} panel The panel to add
36936 * @return {Roo.ContentPanel} The added panel
36938 add : function(target, panel){
36940 target = target.toLowerCase();
36941 return this.regions[target].add(panel);
36945 * Remove a ContentPanel (or subclass) to this layout.
36946 * @param {String} target The target region key (north, south, east, west or center).
36947 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36948 * @return {Roo.ContentPanel} The removed panel
36950 remove : function(target, panel){
36951 target = target.toLowerCase();
36952 return this.regions[target].remove(panel);
36956 * Searches all regions for a panel with the specified id
36957 * @param {String} panelId
36958 * @return {Roo.ContentPanel} The panel or null if it wasn't found
36960 findPanel : function(panelId){
36961 var rs = this.regions;
36962 for(var target in rs){
36963 if(typeof rs[target] != "function"){
36964 var p = rs[target].getPanel(panelId);
36974 * Searches all regions for a panel with the specified id and activates (shows) it.
36975 * @param {String/ContentPanel} panelId The panels id or the panel itself
36976 * @return {Roo.ContentPanel} The shown panel or null
36978 showPanel : function(panelId) {
36979 var rs = this.regions;
36980 for(var target in rs){
36981 var r = rs[target];
36982 if(typeof r != "function"){
36983 if(r.hasPanel(panelId)){
36984 return r.showPanel(panelId);
36992 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36993 * @param {Roo.state.Provider} provider (optional) An alternate state provider
36996 restoreState : function(provider){
36998 provider = Roo.state.Manager;
37000 var sm = new Roo.LayoutStateManager();
37001 sm.init(this, provider);
37007 * Adds a xtype elements to the layout.
37011 xtype : 'ContentPanel',
37018 xtype : 'NestedLayoutPanel',
37024 items : [ ... list of content panels or nested layout panels.. ]
37028 * @param {Object} cfg Xtype definition of item to add.
37030 addxtype : function(cfg)
37032 // basically accepts a pannel...
37033 // can accept a layout region..!?!?
37034 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37037 // theory? children can only be panels??
37039 //if (!cfg.xtype.match(/Panel$/)) {
37044 if (typeof(cfg.region) == 'undefined') {
37045 Roo.log("Failed to add Panel, region was not set");
37049 var region = cfg.region;
37055 xitems = cfg.items;
37060 if ( region == 'center') {
37061 Roo.log("Center: " + cfg.title);
37067 case 'Content': // ContentPanel (el, cfg)
37068 case 'Scroll': // ContentPanel (el, cfg)
37070 cfg.autoCreate = cfg.autoCreate || true;
37071 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37073 // var el = this.el.createChild();
37074 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37077 this.add(region, ret);
37081 case 'TreePanel': // our new panel!
37082 cfg.el = this.el.createChild();
37083 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37084 this.add(region, ret);
37089 // create a new Layout (which is a Border Layout...
37091 var clayout = cfg.layout;
37092 clayout.el = this.el.createChild();
37093 clayout.items = clayout.items || [];
37097 // replace this exitems with the clayout ones..
37098 xitems = clayout.items;
37100 // force background off if it's in center...
37101 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37102 cfg.background = false;
37104 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37107 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37108 //console.log('adding nested layout panel ' + cfg.toSource());
37109 this.add(region, ret);
37110 nb = {}; /// find first...
37115 // needs grid and region
37117 //var el = this.getRegion(region).el.createChild();
37119 *var el = this.el.createChild();
37120 // create the grid first...
37121 cfg.grid.container = el;
37122 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37125 if (region == 'center' && this.active ) {
37126 cfg.background = false;
37129 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37131 this.add(region, ret);
37133 if (cfg.background) {
37134 // render grid on panel activation (if panel background)
37135 ret.on('activate', function(gp) {
37136 if (!gp.grid.rendered) {
37137 // gp.grid.render(el);
37141 // cfg.grid.render(el);
37147 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37148 // it was the old xcomponent building that caused this before.
37149 // espeically if border is the top element in the tree.
37159 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37161 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37162 this.add(region, ret);
37166 throw "Can not add '" + cfg.xtype + "' to Border";
37172 this.beginUpdate();
37176 Roo.each(xitems, function(i) {
37177 region = nb && i.region ? i.region : false;
37179 var add = ret.addxtype(i);
37182 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37183 if (!i.background) {
37184 abn[region] = nb[region] ;
37191 // make the last non-background panel active..
37192 //if (nb) { Roo.log(abn); }
37195 for(var r in abn) {
37196 region = this.getRegion(r);
37198 // tried using nb[r], but it does not work..
37200 region.showPanel(abn[r]);
37211 factory : function(cfg)
37214 var validRegions = Roo.bootstrap.layout.Border.regions;
37216 var target = cfg.region;
37219 var r = Roo.bootstrap.layout;
37223 return new r.North(cfg);
37225 return new r.South(cfg);
37227 return new r.East(cfg);
37229 return new r.West(cfg);
37231 return new r.Center(cfg);
37233 throw 'Layout region "'+target+'" not supported.';
37240 * Ext JS Library 1.1.1
37241 * Copyright(c) 2006-2007, Ext JS, LLC.
37243 * Originally Released Under LGPL - original licence link has changed is not relivant.
37246 * <script type="text/javascript">
37250 * @class Roo.bootstrap.layout.Basic
37251 * @extends Roo.util.Observable
37252 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37253 * and does not have a titlebar, tabs or any other features. All it does is size and position
37254 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37255 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37256 * @cfg {string} region the region that it inhabits..
37257 * @cfg {bool} skipConfig skip config?
37261 Roo.bootstrap.layout.Basic = function(config){
37263 this.mgr = config.mgr;
37265 this.position = config.region;
37267 var skipConfig = config.skipConfig;
37271 * @scope Roo.BasicLayoutRegion
37275 * @event beforeremove
37276 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37277 * @param {Roo.LayoutRegion} this
37278 * @param {Roo.ContentPanel} panel The panel
37279 * @param {Object} e The cancel event object
37281 "beforeremove" : true,
37283 * @event invalidated
37284 * Fires when the layout for this region is changed.
37285 * @param {Roo.LayoutRegion} this
37287 "invalidated" : true,
37289 * @event visibilitychange
37290 * Fires when this region is shown or hidden
37291 * @param {Roo.LayoutRegion} this
37292 * @param {Boolean} visibility true or false
37294 "visibilitychange" : true,
37296 * @event paneladded
37297 * Fires when a panel is added.
37298 * @param {Roo.LayoutRegion} this
37299 * @param {Roo.ContentPanel} panel The panel
37301 "paneladded" : true,
37303 * @event panelremoved
37304 * Fires when a panel is removed.
37305 * @param {Roo.LayoutRegion} this
37306 * @param {Roo.ContentPanel} panel The panel
37308 "panelremoved" : true,
37310 * @event beforecollapse
37311 * Fires when this region before collapse.
37312 * @param {Roo.LayoutRegion} this
37314 "beforecollapse" : true,
37317 * Fires when this region is collapsed.
37318 * @param {Roo.LayoutRegion} this
37320 "collapsed" : true,
37323 * Fires when this region is expanded.
37324 * @param {Roo.LayoutRegion} this
37329 * Fires when this region is slid into view.
37330 * @param {Roo.LayoutRegion} this
37332 "slideshow" : true,
37335 * Fires when this region slides out of view.
37336 * @param {Roo.LayoutRegion} this
37338 "slidehide" : true,
37340 * @event panelactivated
37341 * Fires when a panel is activated.
37342 * @param {Roo.LayoutRegion} this
37343 * @param {Roo.ContentPanel} panel The activated panel
37345 "panelactivated" : true,
37348 * Fires when the user resizes this region.
37349 * @param {Roo.LayoutRegion} this
37350 * @param {Number} newSize The new size (width for east/west, height for north/south)
37354 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37355 this.panels = new Roo.util.MixedCollection();
37356 this.panels.getKey = this.getPanelId.createDelegate(this);
37358 this.activePanel = null;
37359 // ensure listeners are added...
37361 if (config.listeners || config.events) {
37362 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37363 listeners : config.listeners || {},
37364 events : config.events || {}
37368 if(skipConfig !== true){
37369 this.applyConfig(config);
37373 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37375 getPanelId : function(p){
37379 applyConfig : function(config){
37380 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37381 this.config = config;
37386 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37387 * the width, for horizontal (north, south) the height.
37388 * @param {Number} newSize The new width or height
37390 resizeTo : function(newSize){
37391 var el = this.el ? this.el :
37392 (this.activePanel ? this.activePanel.getEl() : null);
37394 switch(this.position){
37397 el.setWidth(newSize);
37398 this.fireEvent("resized", this, newSize);
37402 el.setHeight(newSize);
37403 this.fireEvent("resized", this, newSize);
37409 getBox : function(){
37410 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37413 getMargins : function(){
37414 return this.margins;
37417 updateBox : function(box){
37419 var el = this.activePanel.getEl();
37420 el.dom.style.left = box.x + "px";
37421 el.dom.style.top = box.y + "px";
37422 this.activePanel.setSize(box.width, box.height);
37426 * Returns the container element for this region.
37427 * @return {Roo.Element}
37429 getEl : function(){
37430 return this.activePanel;
37434 * Returns true if this region is currently visible.
37435 * @return {Boolean}
37437 isVisible : function(){
37438 return this.activePanel ? true : false;
37441 setActivePanel : function(panel){
37442 panel = this.getPanel(panel);
37443 if(this.activePanel && this.activePanel != panel){
37444 this.activePanel.setActiveState(false);
37445 this.activePanel.getEl().setLeftTop(-10000,-10000);
37447 this.activePanel = panel;
37448 panel.setActiveState(true);
37450 panel.setSize(this.box.width, this.box.height);
37452 this.fireEvent("panelactivated", this, panel);
37453 this.fireEvent("invalidated");
37457 * Show the specified panel.
37458 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37459 * @return {Roo.ContentPanel} The shown panel or null
37461 showPanel : function(panel){
37462 panel = this.getPanel(panel);
37464 this.setActivePanel(panel);
37470 * Get the active panel for this region.
37471 * @return {Roo.ContentPanel} The active panel or null
37473 getActivePanel : function(){
37474 return this.activePanel;
37478 * Add the passed ContentPanel(s)
37479 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37480 * @return {Roo.ContentPanel} The panel added (if only one was added)
37482 add : function(panel){
37483 if(arguments.length > 1){
37484 for(var i = 0, len = arguments.length; i < len; i++) {
37485 this.add(arguments[i]);
37489 if(this.hasPanel(panel)){
37490 this.showPanel(panel);
37493 var el = panel.getEl();
37494 if(el.dom.parentNode != this.mgr.el.dom){
37495 this.mgr.el.dom.appendChild(el.dom);
37497 if(panel.setRegion){
37498 panel.setRegion(this);
37500 this.panels.add(panel);
37501 el.setStyle("position", "absolute");
37502 if(!panel.background){
37503 this.setActivePanel(panel);
37504 if(this.config.initialSize && this.panels.getCount()==1){
37505 this.resizeTo(this.config.initialSize);
37508 this.fireEvent("paneladded", this, panel);
37513 * Returns true if the panel is in this region.
37514 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37515 * @return {Boolean}
37517 hasPanel : function(panel){
37518 if(typeof panel == "object"){ // must be panel obj
37519 panel = panel.getId();
37521 return this.getPanel(panel) ? true : false;
37525 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37526 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37527 * @param {Boolean} preservePanel Overrides the config preservePanel option
37528 * @return {Roo.ContentPanel} The panel that was removed
37530 remove : function(panel, preservePanel){
37531 panel = this.getPanel(panel);
37536 this.fireEvent("beforeremove", this, panel, e);
37537 if(e.cancel === true){
37540 var panelId = panel.getId();
37541 this.panels.removeKey(panelId);
37546 * Returns the panel specified or null if it's not in this region.
37547 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37548 * @return {Roo.ContentPanel}
37550 getPanel : function(id){
37551 if(typeof id == "object"){ // must be panel obj
37554 return this.panels.get(id);
37558 * Returns this regions position (north/south/east/west/center).
37561 getPosition: function(){
37562 return this.position;
37566 * Ext JS Library 1.1.1
37567 * Copyright(c) 2006-2007, Ext JS, LLC.
37569 * Originally Released Under LGPL - original licence link has changed is not relivant.
37572 * <script type="text/javascript">
37576 * @class Roo.bootstrap.layout.Region
37577 * @extends Roo.bootstrap.layout.Basic
37578 * This class represents a region in a layout manager.
37580 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37581 * @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})
37582 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37583 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37584 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37585 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37586 * @cfg {String} title The title for the region (overrides panel titles)
37587 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37588 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37589 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37590 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37591 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37592 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37593 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37594 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37595 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37596 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37598 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37599 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37600 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37601 * @cfg {Number} width For East/West panels
37602 * @cfg {Number} height For North/South panels
37603 * @cfg {Boolean} split To show the splitter
37604 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37606 * @cfg {string} cls Extra CSS classes to add to region
37608 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37609 * @cfg {string} region the region that it inhabits..
37612 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37613 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37615 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37616 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37617 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37619 Roo.bootstrap.layout.Region = function(config)
37621 this.applyConfig(config);
37623 var mgr = config.mgr;
37624 var pos = config.region;
37625 config.skipConfig = true;
37626 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37629 this.onRender(mgr.el);
37632 this.visible = true;
37633 this.collapsed = false;
37634 this.unrendered_panels = [];
37637 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37639 position: '', // set by wrapper (eg. north/south etc..)
37640 unrendered_panels : null, // unrendered panels.
37642 tabPosition : false,
37644 mgr: false, // points to 'Border'
37647 createBody : function(){
37648 /** This region's body element
37649 * @type Roo.Element */
37650 this.bodyEl = this.el.createChild({
37652 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37656 onRender: function(ctr, pos)
37658 var dh = Roo.DomHelper;
37659 /** This region's container element
37660 * @type Roo.Element */
37661 this.el = dh.append(ctr.dom, {
37663 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37665 /** This region's title element
37666 * @type Roo.Element */
37668 this.titleEl = dh.append(this.el.dom, {
37670 unselectable: "on",
37671 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37673 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37674 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37678 this.titleEl.enableDisplayMode();
37679 /** This region's title text element
37680 * @type HTMLElement */
37681 this.titleTextEl = this.titleEl.dom.firstChild;
37682 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37684 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37685 this.closeBtn.enableDisplayMode();
37686 this.closeBtn.on("click", this.closeClicked, this);
37687 this.closeBtn.hide();
37689 this.createBody(this.config);
37690 if(this.config.hideWhenEmpty){
37692 this.on("paneladded", this.validateVisibility, this);
37693 this.on("panelremoved", this.validateVisibility, this);
37695 if(this.autoScroll){
37696 this.bodyEl.setStyle("overflow", "auto");
37698 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37700 //if(c.titlebar !== false){
37701 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37702 this.titleEl.hide();
37704 this.titleEl.show();
37705 if(this.config.title){
37706 this.titleTextEl.innerHTML = this.config.title;
37710 if(this.config.collapsed){
37711 this.collapse(true);
37713 if(this.config.hidden){
37717 if (this.unrendered_panels && this.unrendered_panels.length) {
37718 for (var i =0;i< this.unrendered_panels.length; i++) {
37719 this.add(this.unrendered_panels[i]);
37721 this.unrendered_panels = null;
37727 applyConfig : function(c)
37730 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37731 var dh = Roo.DomHelper;
37732 if(c.titlebar !== false){
37733 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37734 this.collapseBtn.on("click", this.collapse, this);
37735 this.collapseBtn.enableDisplayMode();
37737 if(c.showPin === true || this.showPin){
37738 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37739 this.stickBtn.enableDisplayMode();
37740 this.stickBtn.on("click", this.expand, this);
37741 this.stickBtn.hide();
37746 /** This region's collapsed element
37747 * @type Roo.Element */
37750 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37751 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37754 if(c.floatable !== false){
37755 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37756 this.collapsedEl.on("click", this.collapseClick, this);
37759 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37760 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37761 id: "message", unselectable: "on", style:{"float":"left"}});
37762 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37764 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37765 this.expandBtn.on("click", this.expand, this);
37769 if(this.collapseBtn){
37770 this.collapseBtn.setVisible(c.collapsible == true);
37773 this.cmargins = c.cmargins || this.cmargins ||
37774 (this.position == "west" || this.position == "east" ?
37775 {top: 0, left: 2, right:2, bottom: 0} :
37776 {top: 2, left: 0, right:0, bottom: 2});
37778 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37781 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37783 this.autoScroll = c.autoScroll || false;
37788 this.duration = c.duration || .30;
37789 this.slideDuration = c.slideDuration || .45;
37794 * Returns true if this region is currently visible.
37795 * @return {Boolean}
37797 isVisible : function(){
37798 return this.visible;
37802 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37803 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37805 //setCollapsedTitle : function(title){
37806 // title = title || " ";
37807 // if(this.collapsedTitleTextEl){
37808 // this.collapsedTitleTextEl.innerHTML = title;
37812 getBox : function(){
37814 // if(!this.collapsed){
37815 b = this.el.getBox(false, true);
37817 // b = this.collapsedEl.getBox(false, true);
37822 getMargins : function(){
37823 return this.margins;
37824 //return this.collapsed ? this.cmargins : this.margins;
37827 highlight : function(){
37828 this.el.addClass("x-layout-panel-dragover");
37831 unhighlight : function(){
37832 this.el.removeClass("x-layout-panel-dragover");
37835 updateBox : function(box)
37837 if (!this.bodyEl) {
37838 return; // not rendered yet..
37842 if(!this.collapsed){
37843 this.el.dom.style.left = box.x + "px";
37844 this.el.dom.style.top = box.y + "px";
37845 this.updateBody(box.width, box.height);
37847 this.collapsedEl.dom.style.left = box.x + "px";
37848 this.collapsedEl.dom.style.top = box.y + "px";
37849 this.collapsedEl.setSize(box.width, box.height);
37852 this.tabs.autoSizeTabs();
37856 updateBody : function(w, h)
37859 this.el.setWidth(w);
37860 w -= this.el.getBorderWidth("rl");
37861 if(this.config.adjustments){
37862 w += this.config.adjustments[0];
37865 if(h !== null && h > 0){
37866 this.el.setHeight(h);
37867 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37868 h -= this.el.getBorderWidth("tb");
37869 if(this.config.adjustments){
37870 h += this.config.adjustments[1];
37872 this.bodyEl.setHeight(h);
37874 h = this.tabs.syncHeight(h);
37877 if(this.panelSize){
37878 w = w !== null ? w : this.panelSize.width;
37879 h = h !== null ? h : this.panelSize.height;
37881 if(this.activePanel){
37882 var el = this.activePanel.getEl();
37883 w = w !== null ? w : el.getWidth();
37884 h = h !== null ? h : el.getHeight();
37885 this.panelSize = {width: w, height: h};
37886 this.activePanel.setSize(w, h);
37888 if(Roo.isIE && this.tabs){
37889 this.tabs.el.repaint();
37894 * Returns the container element for this region.
37895 * @return {Roo.Element}
37897 getEl : function(){
37902 * Hides this region.
37905 //if(!this.collapsed){
37906 this.el.dom.style.left = "-2000px";
37909 // this.collapsedEl.dom.style.left = "-2000px";
37910 // this.collapsedEl.hide();
37912 this.visible = false;
37913 this.fireEvent("visibilitychange", this, false);
37917 * Shows this region if it was previously hidden.
37920 //if(!this.collapsed){
37923 // this.collapsedEl.show();
37925 this.visible = true;
37926 this.fireEvent("visibilitychange", this, true);
37929 closeClicked : function(){
37930 if(this.activePanel){
37931 this.remove(this.activePanel);
37935 collapseClick : function(e){
37937 e.stopPropagation();
37940 e.stopPropagation();
37946 * Collapses this region.
37947 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37950 collapse : function(skipAnim, skipCheck = false){
37951 if(this.collapsed) {
37955 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37957 this.collapsed = true;
37959 this.split.el.hide();
37961 if(this.config.animate && skipAnim !== true){
37962 this.fireEvent("invalidated", this);
37963 this.animateCollapse();
37965 this.el.setLocation(-20000,-20000);
37967 this.collapsedEl.show();
37968 this.fireEvent("collapsed", this);
37969 this.fireEvent("invalidated", this);
37975 animateCollapse : function(){
37980 * Expands this region if it was previously collapsed.
37981 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37982 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37985 expand : function(e, skipAnim){
37987 e.stopPropagation();
37989 if(!this.collapsed || this.el.hasActiveFx()) {
37993 this.afterSlideIn();
37996 this.collapsed = false;
37997 if(this.config.animate && skipAnim !== true){
37998 this.animateExpand();
38002 this.split.el.show();
38004 this.collapsedEl.setLocation(-2000,-2000);
38005 this.collapsedEl.hide();
38006 this.fireEvent("invalidated", this);
38007 this.fireEvent("expanded", this);
38011 animateExpand : function(){
38015 initTabs : function()
38017 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38019 var ts = new Roo.bootstrap.panel.Tabs({
38020 el: this.bodyEl.dom,
38022 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38023 disableTooltips: this.config.disableTabTips,
38024 toolbar : this.config.toolbar
38027 if(this.config.hideTabs){
38028 ts.stripWrap.setDisplayed(false);
38031 ts.resizeTabs = this.config.resizeTabs === true;
38032 ts.minTabWidth = this.config.minTabWidth || 40;
38033 ts.maxTabWidth = this.config.maxTabWidth || 250;
38034 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38035 ts.monitorResize = false;
38036 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38037 ts.bodyEl.addClass('roo-layout-tabs-body');
38038 this.panels.each(this.initPanelAsTab, this);
38041 initPanelAsTab : function(panel){
38042 var ti = this.tabs.addTab(
38046 this.config.closeOnTab && panel.isClosable(),
38049 if(panel.tabTip !== undefined){
38050 ti.setTooltip(panel.tabTip);
38052 ti.on("activate", function(){
38053 this.setActivePanel(panel);
38056 if(this.config.closeOnTab){
38057 ti.on("beforeclose", function(t, e){
38059 this.remove(panel);
38063 panel.tabItem = ti;
38068 updatePanelTitle : function(panel, title)
38070 if(this.activePanel == panel){
38071 this.updateTitle(title);
38074 var ti = this.tabs.getTab(panel.getEl().id);
38076 if(panel.tabTip !== undefined){
38077 ti.setTooltip(panel.tabTip);
38082 updateTitle : function(title){
38083 if(this.titleTextEl && !this.config.title){
38084 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38088 setActivePanel : function(panel)
38090 panel = this.getPanel(panel);
38091 if(this.activePanel && this.activePanel != panel){
38092 if(this.activePanel.setActiveState(false) === false){
38096 this.activePanel = panel;
38097 panel.setActiveState(true);
38098 if(this.panelSize){
38099 panel.setSize(this.panelSize.width, this.panelSize.height);
38102 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38104 this.updateTitle(panel.getTitle());
38106 this.fireEvent("invalidated", this);
38108 this.fireEvent("panelactivated", this, panel);
38112 * Shows the specified panel.
38113 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38114 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38116 showPanel : function(panel)
38118 panel = this.getPanel(panel);
38121 var tab = this.tabs.getTab(panel.getEl().id);
38122 if(tab.isHidden()){
38123 this.tabs.unhideTab(tab.id);
38127 this.setActivePanel(panel);
38134 * Get the active panel for this region.
38135 * @return {Roo.ContentPanel} The active panel or null
38137 getActivePanel : function(){
38138 return this.activePanel;
38141 validateVisibility : function(){
38142 if(this.panels.getCount() < 1){
38143 this.updateTitle(" ");
38144 this.closeBtn.hide();
38147 if(!this.isVisible()){
38154 * Adds the passed ContentPanel(s) to this region.
38155 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38156 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38158 add : function(panel)
38160 if(arguments.length > 1){
38161 for(var i = 0, len = arguments.length; i < len; i++) {
38162 this.add(arguments[i]);
38167 // if we have not been rendered yet, then we can not really do much of this..
38168 if (!this.bodyEl) {
38169 this.unrendered_panels.push(panel);
38176 if(this.hasPanel(panel)){
38177 this.showPanel(panel);
38180 panel.setRegion(this);
38181 this.panels.add(panel);
38182 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38183 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38184 // and hide them... ???
38185 this.bodyEl.dom.appendChild(panel.getEl().dom);
38186 if(panel.background !== true){
38187 this.setActivePanel(panel);
38189 this.fireEvent("paneladded", this, panel);
38196 this.initPanelAsTab(panel);
38200 if(panel.background !== true){
38201 this.tabs.activate(panel.getEl().id);
38203 this.fireEvent("paneladded", this, panel);
38208 * Hides the tab for the specified panel.
38209 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38211 hidePanel : function(panel){
38212 if(this.tabs && (panel = this.getPanel(panel))){
38213 this.tabs.hideTab(panel.getEl().id);
38218 * Unhides the tab for a previously hidden panel.
38219 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38221 unhidePanel : function(panel){
38222 if(this.tabs && (panel = this.getPanel(panel))){
38223 this.tabs.unhideTab(panel.getEl().id);
38227 clearPanels : function(){
38228 while(this.panels.getCount() > 0){
38229 this.remove(this.panels.first());
38234 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38235 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38236 * @param {Boolean} preservePanel Overrides the config preservePanel option
38237 * @return {Roo.ContentPanel} The panel that was removed
38239 remove : function(panel, preservePanel)
38241 panel = this.getPanel(panel);
38246 this.fireEvent("beforeremove", this, panel, e);
38247 if(e.cancel === true){
38250 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38251 var panelId = panel.getId();
38252 this.panels.removeKey(panelId);
38254 document.body.appendChild(panel.getEl().dom);
38257 this.tabs.removeTab(panel.getEl().id);
38258 }else if (!preservePanel){
38259 this.bodyEl.dom.removeChild(panel.getEl().dom);
38261 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38262 var p = this.panels.first();
38263 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38264 tempEl.appendChild(p.getEl().dom);
38265 this.bodyEl.update("");
38266 this.bodyEl.dom.appendChild(p.getEl().dom);
38268 this.updateTitle(p.getTitle());
38270 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38271 this.setActivePanel(p);
38273 panel.setRegion(null);
38274 if(this.activePanel == panel){
38275 this.activePanel = null;
38277 if(this.config.autoDestroy !== false && preservePanel !== true){
38278 try{panel.destroy();}catch(e){}
38280 this.fireEvent("panelremoved", this, panel);
38285 * Returns the TabPanel component used by this region
38286 * @return {Roo.TabPanel}
38288 getTabs : function(){
38292 createTool : function(parentEl, className){
38293 var btn = Roo.DomHelper.append(parentEl, {
38295 cls: "x-layout-tools-button",
38298 cls: "roo-layout-tools-button-inner " + className,
38302 btn.addClassOnOver("roo-layout-tools-button-over");
38307 * Ext JS Library 1.1.1
38308 * Copyright(c) 2006-2007, Ext JS, LLC.
38310 * Originally Released Under LGPL - original licence link has changed is not relivant.
38313 * <script type="text/javascript">
38319 * @class Roo.SplitLayoutRegion
38320 * @extends Roo.LayoutRegion
38321 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38323 Roo.bootstrap.layout.Split = function(config){
38324 this.cursor = config.cursor;
38325 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38328 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38330 splitTip : "Drag to resize.",
38331 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38332 useSplitTips : false,
38334 applyConfig : function(config){
38335 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38338 onRender : function(ctr,pos) {
38340 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38341 if(!this.config.split){
38346 var splitEl = Roo.DomHelper.append(ctr.dom, {
38348 id: this.el.id + "-split",
38349 cls: "roo-layout-split roo-layout-split-"+this.position,
38352 /** The SplitBar for this region
38353 * @type Roo.SplitBar */
38354 // does not exist yet...
38355 Roo.log([this.position, this.orientation]);
38357 this.split = new Roo.bootstrap.SplitBar({
38358 dragElement : splitEl,
38359 resizingElement: this.el,
38360 orientation : this.orientation
38363 this.split.on("moved", this.onSplitMove, this);
38364 this.split.useShim = this.config.useShim === true;
38365 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38366 if(this.useSplitTips){
38367 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38369 //if(config.collapsible){
38370 // this.split.el.on("dblclick", this.collapse, this);
38373 if(typeof this.config.minSize != "undefined"){
38374 this.split.minSize = this.config.minSize;
38376 if(typeof this.config.maxSize != "undefined"){
38377 this.split.maxSize = this.config.maxSize;
38379 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38380 this.hideSplitter();
38385 getHMaxSize : function(){
38386 var cmax = this.config.maxSize || 10000;
38387 var center = this.mgr.getRegion("center");
38388 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38391 getVMaxSize : function(){
38392 var cmax = this.config.maxSize || 10000;
38393 var center = this.mgr.getRegion("center");
38394 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38397 onSplitMove : function(split, newSize){
38398 this.fireEvent("resized", this, newSize);
38402 * Returns the {@link Roo.SplitBar} for this region.
38403 * @return {Roo.SplitBar}
38405 getSplitBar : function(){
38410 this.hideSplitter();
38411 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38414 hideSplitter : function(){
38416 this.split.el.setLocation(-2000,-2000);
38417 this.split.el.hide();
38423 this.split.el.show();
38425 Roo.bootstrap.layout.Split.superclass.show.call(this);
38428 beforeSlide: function(){
38429 if(Roo.isGecko){// firefox overflow auto bug workaround
38430 this.bodyEl.clip();
38432 this.tabs.bodyEl.clip();
38434 if(this.activePanel){
38435 this.activePanel.getEl().clip();
38437 if(this.activePanel.beforeSlide){
38438 this.activePanel.beforeSlide();
38444 afterSlide : function(){
38445 if(Roo.isGecko){// firefox overflow auto bug workaround
38446 this.bodyEl.unclip();
38448 this.tabs.bodyEl.unclip();
38450 if(this.activePanel){
38451 this.activePanel.getEl().unclip();
38452 if(this.activePanel.afterSlide){
38453 this.activePanel.afterSlide();
38459 initAutoHide : function(){
38460 if(this.autoHide !== false){
38461 if(!this.autoHideHd){
38462 var st = new Roo.util.DelayedTask(this.slideIn, this);
38463 this.autoHideHd = {
38464 "mouseout": function(e){
38465 if(!e.within(this.el, true)){
38469 "mouseover" : function(e){
38475 this.el.on(this.autoHideHd);
38479 clearAutoHide : function(){
38480 if(this.autoHide !== false){
38481 this.el.un("mouseout", this.autoHideHd.mouseout);
38482 this.el.un("mouseover", this.autoHideHd.mouseover);
38486 clearMonitor : function(){
38487 Roo.get(document).un("click", this.slideInIf, this);
38490 // these names are backwards but not changed for compat
38491 slideOut : function(){
38492 if(this.isSlid || this.el.hasActiveFx()){
38495 this.isSlid = true;
38496 if(this.collapseBtn){
38497 this.collapseBtn.hide();
38499 this.closeBtnState = this.closeBtn.getStyle('display');
38500 this.closeBtn.hide();
38502 this.stickBtn.show();
38505 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38506 this.beforeSlide();
38507 this.el.setStyle("z-index", 10001);
38508 this.el.slideIn(this.getSlideAnchor(), {
38509 callback: function(){
38511 this.initAutoHide();
38512 Roo.get(document).on("click", this.slideInIf, this);
38513 this.fireEvent("slideshow", this);
38520 afterSlideIn : function(){
38521 this.clearAutoHide();
38522 this.isSlid = false;
38523 this.clearMonitor();
38524 this.el.setStyle("z-index", "");
38525 if(this.collapseBtn){
38526 this.collapseBtn.show();
38528 this.closeBtn.setStyle('display', this.closeBtnState);
38530 this.stickBtn.hide();
38532 this.fireEvent("slidehide", this);
38535 slideIn : function(cb){
38536 if(!this.isSlid || this.el.hasActiveFx()){
38540 this.isSlid = false;
38541 this.beforeSlide();
38542 this.el.slideOut(this.getSlideAnchor(), {
38543 callback: function(){
38544 this.el.setLeftTop(-10000, -10000);
38546 this.afterSlideIn();
38554 slideInIf : function(e){
38555 if(!e.within(this.el)){
38560 animateCollapse : function(){
38561 this.beforeSlide();
38562 this.el.setStyle("z-index", 20000);
38563 var anchor = this.getSlideAnchor();
38564 this.el.slideOut(anchor, {
38565 callback : function(){
38566 this.el.setStyle("z-index", "");
38567 this.collapsedEl.slideIn(anchor, {duration:.3});
38569 this.el.setLocation(-10000,-10000);
38571 this.fireEvent("collapsed", this);
38578 animateExpand : function(){
38579 this.beforeSlide();
38580 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38581 this.el.setStyle("z-index", 20000);
38582 this.collapsedEl.hide({
38585 this.el.slideIn(this.getSlideAnchor(), {
38586 callback : function(){
38587 this.el.setStyle("z-index", "");
38590 this.split.el.show();
38592 this.fireEvent("invalidated", this);
38593 this.fireEvent("expanded", this);
38621 getAnchor : function(){
38622 return this.anchors[this.position];
38625 getCollapseAnchor : function(){
38626 return this.canchors[this.position];
38629 getSlideAnchor : function(){
38630 return this.sanchors[this.position];
38633 getAlignAdj : function(){
38634 var cm = this.cmargins;
38635 switch(this.position){
38651 getExpandAdj : function(){
38652 var c = this.collapsedEl, cm = this.cmargins;
38653 switch(this.position){
38655 return [-(cm.right+c.getWidth()+cm.left), 0];
38658 return [cm.right+c.getWidth()+cm.left, 0];
38661 return [0, -(cm.top+cm.bottom+c.getHeight())];
38664 return [0, cm.top+cm.bottom+c.getHeight()];
38670 * Ext JS Library 1.1.1
38671 * Copyright(c) 2006-2007, Ext JS, LLC.
38673 * Originally Released Under LGPL - original licence link has changed is not relivant.
38676 * <script type="text/javascript">
38679 * These classes are private internal classes
38681 Roo.bootstrap.layout.Center = function(config){
38682 config.region = "center";
38683 Roo.bootstrap.layout.Region.call(this, config);
38684 this.visible = true;
38685 this.minWidth = config.minWidth || 20;
38686 this.minHeight = config.minHeight || 20;
38689 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38691 // center panel can't be hidden
38695 // center panel can't be hidden
38698 getMinWidth: function(){
38699 return this.minWidth;
38702 getMinHeight: function(){
38703 return this.minHeight;
38717 Roo.bootstrap.layout.North = function(config)
38719 config.region = 'north';
38720 config.cursor = 'n-resize';
38722 Roo.bootstrap.layout.Split.call(this, config);
38726 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38727 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38728 this.split.el.addClass("roo-layout-split-v");
38730 var size = config.initialSize || config.height;
38731 if(typeof size != "undefined"){
38732 this.el.setHeight(size);
38735 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38737 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38741 getBox : function(){
38742 if(this.collapsed){
38743 return this.collapsedEl.getBox();
38745 var box = this.el.getBox();
38747 box.height += this.split.el.getHeight();
38752 updateBox : function(box){
38753 if(this.split && !this.collapsed){
38754 box.height -= this.split.el.getHeight();
38755 this.split.el.setLeft(box.x);
38756 this.split.el.setTop(box.y+box.height);
38757 this.split.el.setWidth(box.width);
38759 if(this.collapsed){
38760 this.updateBody(box.width, null);
38762 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38770 Roo.bootstrap.layout.South = function(config){
38771 config.region = 'south';
38772 config.cursor = 's-resize';
38773 Roo.bootstrap.layout.Split.call(this, config);
38775 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38776 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38777 this.split.el.addClass("roo-layout-split-v");
38779 var size = config.initialSize || config.height;
38780 if(typeof size != "undefined"){
38781 this.el.setHeight(size);
38785 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38786 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38787 getBox : function(){
38788 if(this.collapsed){
38789 return this.collapsedEl.getBox();
38791 var box = this.el.getBox();
38793 var sh = this.split.el.getHeight();
38800 updateBox : function(box){
38801 if(this.split && !this.collapsed){
38802 var sh = this.split.el.getHeight();
38805 this.split.el.setLeft(box.x);
38806 this.split.el.setTop(box.y-sh);
38807 this.split.el.setWidth(box.width);
38809 if(this.collapsed){
38810 this.updateBody(box.width, null);
38812 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38816 Roo.bootstrap.layout.East = function(config){
38817 config.region = "east";
38818 config.cursor = "e-resize";
38819 Roo.bootstrap.layout.Split.call(this, config);
38821 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38822 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38823 this.split.el.addClass("roo-layout-split-h");
38825 var size = config.initialSize || config.width;
38826 if(typeof size != "undefined"){
38827 this.el.setWidth(size);
38830 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38831 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38832 getBox : function(){
38833 if(this.collapsed){
38834 return this.collapsedEl.getBox();
38836 var box = this.el.getBox();
38838 var sw = this.split.el.getWidth();
38845 updateBox : function(box){
38846 if(this.split && !this.collapsed){
38847 var sw = this.split.el.getWidth();
38849 this.split.el.setLeft(box.x);
38850 this.split.el.setTop(box.y);
38851 this.split.el.setHeight(box.height);
38854 if(this.collapsed){
38855 this.updateBody(null, box.height);
38857 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38861 Roo.bootstrap.layout.West = function(config){
38862 config.region = "west";
38863 config.cursor = "w-resize";
38865 Roo.bootstrap.layout.Split.call(this, config);
38867 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38868 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38869 this.split.el.addClass("roo-layout-split-h");
38873 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38874 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38876 onRender: function(ctr, pos)
38878 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38879 var size = this.config.initialSize || this.config.width;
38880 if(typeof size != "undefined"){
38881 this.el.setWidth(size);
38885 getBox : function(){
38886 if(this.collapsed){
38887 return this.collapsedEl.getBox();
38889 var box = this.el.getBox();
38891 box.width += this.split.el.getWidth();
38896 updateBox : function(box){
38897 if(this.split && !this.collapsed){
38898 var sw = this.split.el.getWidth();
38900 this.split.el.setLeft(box.x+box.width);
38901 this.split.el.setTop(box.y);
38902 this.split.el.setHeight(box.height);
38904 if(this.collapsed){
38905 this.updateBody(null, box.height);
38907 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38909 });Roo.namespace("Roo.bootstrap.panel");/*
38911 * Ext JS Library 1.1.1
38912 * Copyright(c) 2006-2007, Ext JS, LLC.
38914 * Originally Released Under LGPL - original licence link has changed is not relivant.
38917 * <script type="text/javascript">
38920 * @class Roo.ContentPanel
38921 * @extends Roo.util.Observable
38922 * A basic ContentPanel element.
38923 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38924 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38925 * @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
38926 * @cfg {Boolean} closable True if the panel can be closed/removed
38927 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38928 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38929 * @cfg {Toolbar} toolbar A toolbar for this panel
38930 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38931 * @cfg {String} title The title for this panel
38932 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38933 * @cfg {String} url Calls {@link #setUrl} with this value
38934 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38935 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38936 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38937 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38938 * @cfg {Boolean} badges render the badges
38941 * Create a new ContentPanel.
38942 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38943 * @param {String/Object} config A string to set only the title or a config object
38944 * @param {String} content (optional) Set the HTML content for this panel
38945 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38947 Roo.bootstrap.panel.Content = function( config){
38949 this.tpl = config.tpl || false;
38951 var el = config.el;
38952 var content = config.content;
38954 if(config.autoCreate){ // xtype is available if this is called from factory
38957 this.el = Roo.get(el);
38958 if(!this.el && config && config.autoCreate){
38959 if(typeof config.autoCreate == "object"){
38960 if(!config.autoCreate.id){
38961 config.autoCreate.id = config.id||el;
38963 this.el = Roo.DomHelper.append(document.body,
38964 config.autoCreate, true);
38966 var elcfg = { tag: "div",
38967 cls: "roo-layout-inactive-content",
38971 elcfg.html = config.html;
38975 this.el = Roo.DomHelper.append(document.body, elcfg , true);
38978 this.closable = false;
38979 this.loaded = false;
38980 this.active = false;
38983 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38985 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38987 this.wrapEl = this.el; //this.el.wrap();
38989 if (config.toolbar.items) {
38990 ti = config.toolbar.items ;
38991 delete config.toolbar.items ;
38995 this.toolbar.render(this.wrapEl, 'before');
38996 for(var i =0;i < ti.length;i++) {
38997 // Roo.log(['add child', items[i]]);
38998 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39000 this.toolbar.items = nitems;
39001 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39002 delete config.toolbar;
39006 // xtype created footer. - not sure if will work as we normally have to render first..
39007 if (this.footer && !this.footer.el && this.footer.xtype) {
39008 if (!this.wrapEl) {
39009 this.wrapEl = this.el.wrap();
39012 this.footer.container = this.wrapEl.createChild();
39014 this.footer = Roo.factory(this.footer, Roo);
39019 if(typeof config == "string"){
39020 this.title = config;
39022 Roo.apply(this, config);
39026 this.resizeEl = Roo.get(this.resizeEl, true);
39028 this.resizeEl = this.el;
39030 // handle view.xtype
39038 * Fires when this panel is activated.
39039 * @param {Roo.ContentPanel} this
39043 * @event deactivate
39044 * Fires when this panel is activated.
39045 * @param {Roo.ContentPanel} this
39047 "deactivate" : true,
39051 * Fires when this panel is resized if fitToFrame is true.
39052 * @param {Roo.ContentPanel} this
39053 * @param {Number} width The width after any component adjustments
39054 * @param {Number} height The height after any component adjustments
39060 * Fires when this tab is created
39061 * @param {Roo.ContentPanel} this
39072 if(this.autoScroll){
39073 this.resizeEl.setStyle("overflow", "auto");
39075 // fix randome scrolling
39076 //this.el.on('scroll', function() {
39077 // Roo.log('fix random scolling');
39078 // this.scrollTo('top',0);
39081 content = content || this.content;
39083 this.setContent(content);
39085 if(config && config.url){
39086 this.setUrl(this.url, this.params, this.loadOnce);
39091 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39093 if (this.view && typeof(this.view.xtype) != 'undefined') {
39094 this.view.el = this.el.appendChild(document.createElement("div"));
39095 this.view = Roo.factory(this.view);
39096 this.view.render && this.view.render(false, '');
39100 this.fireEvent('render', this);
39103 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39107 setRegion : function(region){
39108 this.region = region;
39109 this.setActiveClass(region && !this.background);
39113 setActiveClass: function(state)
39116 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39117 this.el.setStyle('position','relative');
39119 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39120 this.el.setStyle('position', 'absolute');
39125 * Returns the toolbar for this Panel if one was configured.
39126 * @return {Roo.Toolbar}
39128 getToolbar : function(){
39129 return this.toolbar;
39132 setActiveState : function(active)
39134 this.active = active;
39135 this.setActiveClass(active);
39137 if(this.fireEvent("deactivate", this) === false){
39142 this.fireEvent("activate", this);
39146 * Updates this panel's element
39147 * @param {String} content The new content
39148 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39150 setContent : function(content, loadScripts){
39151 this.el.update(content, loadScripts);
39154 ignoreResize : function(w, h){
39155 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39158 this.lastSize = {width: w, height: h};
39163 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39164 * @return {Roo.UpdateManager} The UpdateManager
39166 getUpdateManager : function(){
39167 return this.el.getUpdateManager();
39170 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39171 * @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:
39174 url: "your-url.php",
39175 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39176 callback: yourFunction,
39177 scope: yourObject, //(optional scope)
39180 text: "Loading...",
39185 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39186 * 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.
39187 * @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}
39188 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39189 * @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.
39190 * @return {Roo.ContentPanel} this
39193 var um = this.el.getUpdateManager();
39194 um.update.apply(um, arguments);
39200 * 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.
39201 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39202 * @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)
39203 * @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)
39204 * @return {Roo.UpdateManager} The UpdateManager
39206 setUrl : function(url, params, loadOnce){
39207 if(this.refreshDelegate){
39208 this.removeListener("activate", this.refreshDelegate);
39210 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39211 this.on("activate", this.refreshDelegate);
39212 return this.el.getUpdateManager();
39215 _handleRefresh : function(url, params, loadOnce){
39216 if(!loadOnce || !this.loaded){
39217 var updater = this.el.getUpdateManager();
39218 updater.update(url, params, this._setLoaded.createDelegate(this));
39222 _setLoaded : function(){
39223 this.loaded = true;
39227 * Returns this panel's id
39230 getId : function(){
39235 * Returns this panel's element - used by regiosn to add.
39236 * @return {Roo.Element}
39238 getEl : function(){
39239 return this.wrapEl || this.el;
39244 adjustForComponents : function(width, height)
39246 //Roo.log('adjustForComponents ');
39247 if(this.resizeEl != this.el){
39248 width -= this.el.getFrameWidth('lr');
39249 height -= this.el.getFrameWidth('tb');
39252 var te = this.toolbar.getEl();
39253 te.setWidth(width);
39254 height -= te.getHeight();
39257 var te = this.footer.getEl();
39258 te.setWidth(width);
39259 height -= te.getHeight();
39263 if(this.adjustments){
39264 width += this.adjustments[0];
39265 height += this.adjustments[1];
39267 return {"width": width, "height": height};
39270 setSize : function(width, height){
39271 if(this.fitToFrame && !this.ignoreResize(width, height)){
39272 if(this.fitContainer && this.resizeEl != this.el){
39273 this.el.setSize(width, height);
39275 var size = this.adjustForComponents(width, height);
39276 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39277 this.fireEvent('resize', this, size.width, size.height);
39282 * Returns this panel's title
39285 getTitle : function(){
39287 if (typeof(this.title) != 'object') {
39292 for (var k in this.title) {
39293 if (!this.title.hasOwnProperty(k)) {
39297 if (k.indexOf('-') >= 0) {
39298 var s = k.split('-');
39299 for (var i = 0; i<s.length; i++) {
39300 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39303 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39310 * Set this panel's title
39311 * @param {String} title
39313 setTitle : function(title){
39314 this.title = title;
39316 this.region.updatePanelTitle(this, title);
39321 * Returns true is this panel was configured to be closable
39322 * @return {Boolean}
39324 isClosable : function(){
39325 return this.closable;
39328 beforeSlide : function(){
39330 this.resizeEl.clip();
39333 afterSlide : function(){
39335 this.resizeEl.unclip();
39339 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39340 * Will fail silently if the {@link #setUrl} method has not been called.
39341 * This does not activate the panel, just updates its content.
39343 refresh : function(){
39344 if(this.refreshDelegate){
39345 this.loaded = false;
39346 this.refreshDelegate();
39351 * Destroys this panel
39353 destroy : function(){
39354 this.el.removeAllListeners();
39355 var tempEl = document.createElement("span");
39356 tempEl.appendChild(this.el.dom);
39357 tempEl.innerHTML = "";
39363 * form - if the content panel contains a form - this is a reference to it.
39364 * @type {Roo.form.Form}
39368 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39369 * This contains a reference to it.
39375 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39385 * @param {Object} cfg Xtype definition of item to add.
39389 getChildContainer: function () {
39390 return this.getEl();
39395 var ret = new Roo.factory(cfg);
39400 if (cfg.xtype.match(/^Form$/)) {
39403 //if (this.footer) {
39404 // el = this.footer.container.insertSibling(false, 'before');
39406 el = this.el.createChild();
39409 this.form = new Roo.form.Form(cfg);
39412 if ( this.form.allItems.length) {
39413 this.form.render(el.dom);
39417 // should only have one of theses..
39418 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39419 // views.. should not be just added - used named prop 'view''
39421 cfg.el = this.el.appendChild(document.createElement("div"));
39424 var ret = new Roo.factory(cfg);
39426 ret.render && ret.render(false, ''); // render blank..
39436 * @class Roo.bootstrap.panel.Grid
39437 * @extends Roo.bootstrap.panel.Content
39439 * Create a new GridPanel.
39440 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39441 * @param {Object} config A the config object
39447 Roo.bootstrap.panel.Grid = function(config)
39451 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39452 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39454 config.el = this.wrapper;
39455 //this.el = this.wrapper;
39457 if (config.container) {
39458 // ctor'ed from a Border/panel.grid
39461 this.wrapper.setStyle("overflow", "hidden");
39462 this.wrapper.addClass('roo-grid-container');
39467 if(config.toolbar){
39468 var tool_el = this.wrapper.createChild();
39469 this.toolbar = Roo.factory(config.toolbar);
39471 if (config.toolbar.items) {
39472 ti = config.toolbar.items ;
39473 delete config.toolbar.items ;
39477 this.toolbar.render(tool_el);
39478 for(var i =0;i < ti.length;i++) {
39479 // Roo.log(['add child', items[i]]);
39480 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39482 this.toolbar.items = nitems;
39484 delete config.toolbar;
39487 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39488 config.grid.scrollBody = true;;
39489 config.grid.monitorWindowResize = false; // turn off autosizing
39490 config.grid.autoHeight = false;
39491 config.grid.autoWidth = false;
39493 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39495 if (config.background) {
39496 // render grid on panel activation (if panel background)
39497 this.on('activate', function(gp) {
39498 if (!gp.grid.rendered) {
39499 gp.grid.render(this.wrapper);
39500 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39505 this.grid.render(this.wrapper);
39506 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39509 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39510 // ??? needed ??? config.el = this.wrapper;
39515 // xtype created footer. - not sure if will work as we normally have to render first..
39516 if (this.footer && !this.footer.el && this.footer.xtype) {
39518 var ctr = this.grid.getView().getFooterPanel(true);
39519 this.footer.dataSource = this.grid.dataSource;
39520 this.footer = Roo.factory(this.footer, Roo);
39521 this.footer.render(ctr);
39531 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39532 getId : function(){
39533 return this.grid.id;
39537 * Returns the grid for this panel
39538 * @return {Roo.bootstrap.Table}
39540 getGrid : function(){
39544 setSize : function(width, height){
39545 if(!this.ignoreResize(width, height)){
39546 var grid = this.grid;
39547 var size = this.adjustForComponents(width, height);
39548 var gridel = grid.getGridEl();
39549 gridel.setSize(size.width, size.height);
39551 var thd = grid.getGridEl().select('thead',true).first();
39552 var tbd = grid.getGridEl().select('tbody', true).first();
39554 tbd.setSize(width, height - thd.getHeight());
39563 beforeSlide : function(){
39564 this.grid.getView().scroller.clip();
39567 afterSlide : function(){
39568 this.grid.getView().scroller.unclip();
39571 destroy : function(){
39572 this.grid.destroy();
39574 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39579 * @class Roo.bootstrap.panel.Nest
39580 * @extends Roo.bootstrap.panel.Content
39582 * Create a new Panel, that can contain a layout.Border.
39585 * @param {Roo.BorderLayout} layout The layout for this panel
39586 * @param {String/Object} config A string to set only the title or a config object
39588 Roo.bootstrap.panel.Nest = function(config)
39590 // construct with only one argument..
39591 /* FIXME - implement nicer consturctors
39592 if (layout.layout) {
39594 layout = config.layout;
39595 delete config.layout;
39597 if (layout.xtype && !layout.getEl) {
39598 // then layout needs constructing..
39599 layout = Roo.factory(layout, Roo);
39603 config.el = config.layout.getEl();
39605 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39607 config.layout.monitorWindowResize = false; // turn off autosizing
39608 this.layout = config.layout;
39609 this.layout.getEl().addClass("roo-layout-nested-layout");
39610 this.layout.parent = this;
39617 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39619 setSize : function(width, height){
39620 if(!this.ignoreResize(width, height)){
39621 var size = this.adjustForComponents(width, height);
39622 var el = this.layout.getEl();
39623 if (size.height < 1) {
39624 el.setWidth(size.width);
39626 el.setSize(size.width, size.height);
39628 var touch = el.dom.offsetWidth;
39629 this.layout.layout();
39630 // ie requires a double layout on the first pass
39631 if(Roo.isIE && !this.initialized){
39632 this.initialized = true;
39633 this.layout.layout();
39638 // activate all subpanels if not currently active..
39640 setActiveState : function(active){
39641 this.active = active;
39642 this.setActiveClass(active);
39645 this.fireEvent("deactivate", this);
39649 this.fireEvent("activate", this);
39650 // not sure if this should happen before or after..
39651 if (!this.layout) {
39652 return; // should not happen..
39655 for (var r in this.layout.regions) {
39656 reg = this.layout.getRegion(r);
39657 if (reg.getActivePanel()) {
39658 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39659 reg.setActivePanel(reg.getActivePanel());
39662 if (!reg.panels.length) {
39665 reg.showPanel(reg.getPanel(0));
39674 * Returns the nested BorderLayout for this panel
39675 * @return {Roo.BorderLayout}
39677 getLayout : function(){
39678 return this.layout;
39682 * Adds a xtype elements to the layout of the nested panel
39686 xtype : 'ContentPanel',
39693 xtype : 'NestedLayoutPanel',
39699 items : [ ... list of content panels or nested layout panels.. ]
39703 * @param {Object} cfg Xtype definition of item to add.
39705 addxtype : function(cfg) {
39706 return this.layout.addxtype(cfg);
39711 * Ext JS Library 1.1.1
39712 * Copyright(c) 2006-2007, Ext JS, LLC.
39714 * Originally Released Under LGPL - original licence link has changed is not relivant.
39717 * <script type="text/javascript">
39720 * @class Roo.TabPanel
39721 * @extends Roo.util.Observable
39722 * A lightweight tab container.
39726 // basic tabs 1, built from existing content
39727 var tabs = new Roo.TabPanel("tabs1");
39728 tabs.addTab("script", "View Script");
39729 tabs.addTab("markup", "View Markup");
39730 tabs.activate("script");
39732 // more advanced tabs, built from javascript
39733 var jtabs = new Roo.TabPanel("jtabs");
39734 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39736 // set up the UpdateManager
39737 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39738 var updater = tab2.getUpdateManager();
39739 updater.setDefaultUrl("ajax1.htm");
39740 tab2.on('activate', updater.refresh, updater, true);
39742 // Use setUrl for Ajax loading
39743 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39744 tab3.setUrl("ajax2.htm", null, true);
39747 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39750 jtabs.activate("jtabs-1");
39753 * Create a new TabPanel.
39754 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39755 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39757 Roo.bootstrap.panel.Tabs = function(config){
39759 * The container element for this TabPanel.
39760 * @type Roo.Element
39762 this.el = Roo.get(config.el);
39765 if(typeof config == "boolean"){
39766 this.tabPosition = config ? "bottom" : "top";
39768 Roo.apply(this, config);
39772 if(this.tabPosition == "bottom"){
39773 // if tabs are at the bottom = create the body first.
39774 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39775 this.el.addClass("roo-tabs-bottom");
39777 // next create the tabs holders
39779 if (this.tabPosition == "west"){
39781 var reg = this.region; // fake it..
39783 if (!reg.mgr.parent) {
39786 reg = reg.mgr.parent.region;
39788 Roo.log("got nest?");
39790 if (reg.mgr.getRegion('west')) {
39791 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39792 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39793 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39794 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39795 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39803 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39804 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39805 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39806 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39811 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39814 // finally - if tabs are at the top, then create the body last..
39815 if(this.tabPosition != "bottom"){
39816 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39817 * @type Roo.Element
39819 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39820 this.el.addClass("roo-tabs-top");
39824 this.bodyEl.setStyle("position", "relative");
39826 this.active = null;
39827 this.activateDelegate = this.activate.createDelegate(this);
39832 * Fires when the active tab changes
39833 * @param {Roo.TabPanel} this
39834 * @param {Roo.TabPanelItem} activePanel The new active tab
39838 * @event beforetabchange
39839 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39840 * @param {Roo.TabPanel} this
39841 * @param {Object} e Set cancel to true on this object to cancel the tab change
39842 * @param {Roo.TabPanelItem} tab The tab being changed to
39844 "beforetabchange" : true
39847 Roo.EventManager.onWindowResize(this.onResize, this);
39848 this.cpad = this.el.getPadding("lr");
39849 this.hiddenCount = 0;
39852 // toolbar on the tabbar support...
39853 if (this.toolbar) {
39854 alert("no toolbar support yet");
39855 this.toolbar = false;
39857 var tcfg = this.toolbar;
39858 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39859 this.toolbar = new Roo.Toolbar(tcfg);
39860 if (Roo.isSafari) {
39861 var tbl = tcfg.container.child('table', true);
39862 tbl.setAttribute('width', '100%');
39870 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39873 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39875 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39877 tabPosition : "top",
39879 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39881 currentTabWidth : 0,
39883 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39887 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39891 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39893 preferredTabWidth : 175,
39895 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39897 resizeTabs : false,
39899 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39901 monitorResize : true,
39903 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39905 toolbar : false, // set by caller..
39907 region : false, /// set by caller
39909 disableTooltips : true, // not used yet...
39912 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39913 * @param {String} id The id of the div to use <b>or create</b>
39914 * @param {String} text The text for the tab
39915 * @param {String} content (optional) Content to put in the TabPanelItem body
39916 * @param {Boolean} closable (optional) True to create a close icon on the tab
39917 * @return {Roo.TabPanelItem} The created TabPanelItem
39919 addTab : function(id, text, content, closable, tpl)
39921 var item = new Roo.bootstrap.panel.TabItem({
39925 closable : closable,
39928 this.addTabItem(item);
39930 item.setContent(content);
39936 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39937 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39938 * @return {Roo.TabPanelItem}
39940 getTab : function(id){
39941 return this.items[id];
39945 * Hides the {@link Roo.TabPanelItem} with the specified id/index
39946 * @param {String/Number} id The id or index of the TabPanelItem to hide.
39948 hideTab : function(id){
39949 var t = this.items[id];
39952 this.hiddenCount++;
39953 this.autoSizeTabs();
39958 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39959 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39961 unhideTab : function(id){
39962 var t = this.items[id];
39964 t.setHidden(false);
39965 this.hiddenCount--;
39966 this.autoSizeTabs();
39971 * Adds an existing {@link Roo.TabPanelItem}.
39972 * @param {Roo.TabPanelItem} item The TabPanelItem to add
39974 addTabItem : function(item)
39976 this.items[item.id] = item;
39977 this.items.push(item);
39978 this.autoSizeTabs();
39979 // if(this.resizeTabs){
39980 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39981 // this.autoSizeTabs();
39983 // item.autoSize();
39988 * Removes a {@link Roo.TabPanelItem}.
39989 * @param {String/Number} id The id or index of the TabPanelItem to remove.
39991 removeTab : function(id){
39992 var items = this.items;
39993 var tab = items[id];
39994 if(!tab) { return; }
39995 var index = items.indexOf(tab);
39996 if(this.active == tab && items.length > 1){
39997 var newTab = this.getNextAvailable(index);
40002 this.stripEl.dom.removeChild(tab.pnode.dom);
40003 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40004 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40006 items.splice(index, 1);
40007 delete this.items[tab.id];
40008 tab.fireEvent("close", tab);
40009 tab.purgeListeners();
40010 this.autoSizeTabs();
40013 getNextAvailable : function(start){
40014 var items = this.items;
40016 // look for a next tab that will slide over to
40017 // replace the one being removed
40018 while(index < items.length){
40019 var item = items[++index];
40020 if(item && !item.isHidden()){
40024 // if one isn't found select the previous tab (on the left)
40027 var item = items[--index];
40028 if(item && !item.isHidden()){
40036 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40037 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40039 disableTab : function(id){
40040 var tab = this.items[id];
40041 if(tab && this.active != tab){
40047 * Enables a {@link Roo.TabPanelItem} that is disabled.
40048 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40050 enableTab : function(id){
40051 var tab = this.items[id];
40056 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40057 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40058 * @return {Roo.TabPanelItem} The TabPanelItem.
40060 activate : function(id)
40062 //Roo.log('activite:' + id);
40064 var tab = this.items[id];
40068 if(tab == this.active || tab.disabled){
40072 this.fireEvent("beforetabchange", this, e, tab);
40073 if(e.cancel !== true && !tab.disabled){
40075 this.active.hide();
40077 this.active = this.items[id];
40078 this.active.show();
40079 this.fireEvent("tabchange", this, this.active);
40085 * Gets the active {@link Roo.TabPanelItem}.
40086 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40088 getActiveTab : function(){
40089 return this.active;
40093 * Updates the tab body element to fit the height of the container element
40094 * for overflow scrolling
40095 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40097 syncHeight : function(targetHeight){
40098 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40099 var bm = this.bodyEl.getMargins();
40100 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40101 this.bodyEl.setHeight(newHeight);
40105 onResize : function(){
40106 if(this.monitorResize){
40107 this.autoSizeTabs();
40112 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40114 beginUpdate : function(){
40115 this.updating = true;
40119 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40121 endUpdate : function(){
40122 this.updating = false;
40123 this.autoSizeTabs();
40127 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40129 autoSizeTabs : function()
40131 var count = this.items.length;
40132 var vcount = count - this.hiddenCount;
40135 this.stripEl.hide();
40137 this.stripEl.show();
40140 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40145 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40146 var availWidth = Math.floor(w / vcount);
40147 var b = this.stripBody;
40148 if(b.getWidth() > w){
40149 var tabs = this.items;
40150 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40151 if(availWidth < this.minTabWidth){
40152 /*if(!this.sleft){ // incomplete scrolling code
40153 this.createScrollButtons();
40156 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40159 if(this.currentTabWidth < this.preferredTabWidth){
40160 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40166 * Returns the number of tabs in this TabPanel.
40169 getCount : function(){
40170 return this.items.length;
40174 * Resizes all the tabs to the passed width
40175 * @param {Number} The new width
40177 setTabWidth : function(width){
40178 this.currentTabWidth = width;
40179 for(var i = 0, len = this.items.length; i < len; i++) {
40180 if(!this.items[i].isHidden()) {
40181 this.items[i].setWidth(width);
40187 * Destroys this TabPanel
40188 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40190 destroy : function(removeEl){
40191 Roo.EventManager.removeResizeListener(this.onResize, this);
40192 for(var i = 0, len = this.items.length; i < len; i++){
40193 this.items[i].purgeListeners();
40195 if(removeEl === true){
40196 this.el.update("");
40201 createStrip : function(container)
40203 var strip = document.createElement("nav");
40204 strip.className = Roo.bootstrap.version == 4 ?
40205 "navbar-light bg-light" :
40206 "navbar navbar-default"; //"x-tabs-wrap";
40207 container.appendChild(strip);
40211 createStripList : function(strip)
40213 // div wrapper for retard IE
40214 // returns the "tr" element.
40215 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40216 //'<div class="x-tabs-strip-wrap">'+
40217 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40218 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40219 return strip.firstChild; //.firstChild.firstChild.firstChild;
40221 createBody : function(container)
40223 var body = document.createElement("div");
40224 Roo.id(body, "tab-body");
40225 //Roo.fly(body).addClass("x-tabs-body");
40226 Roo.fly(body).addClass("tab-content");
40227 container.appendChild(body);
40230 createItemBody :function(bodyEl, id){
40231 var body = Roo.getDom(id);
40233 body = document.createElement("div");
40236 //Roo.fly(body).addClass("x-tabs-item-body");
40237 Roo.fly(body).addClass("tab-pane");
40238 bodyEl.insertBefore(body, bodyEl.firstChild);
40242 createStripElements : function(stripEl, text, closable, tpl)
40244 var td = document.createElement("li"); // was td..
40245 td.className = 'nav-item';
40247 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40250 stripEl.appendChild(td);
40252 td.className = "x-tabs-closable";
40253 if(!this.closeTpl){
40254 this.closeTpl = new Roo.Template(
40255 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40256 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40257 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40260 var el = this.closeTpl.overwrite(td, {"text": text});
40261 var close = el.getElementsByTagName("div")[0];
40262 var inner = el.getElementsByTagName("em")[0];
40263 return {"el": el, "close": close, "inner": inner};
40266 // not sure what this is..
40267 // if(!this.tabTpl){
40268 //this.tabTpl = new Roo.Template(
40269 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40270 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40272 // this.tabTpl = new Roo.Template(
40273 // '<a href="#">' +
40274 // '<span unselectable="on"' +
40275 // (this.disableTooltips ? '' : ' title="{text}"') +
40276 // ' >{text}</span></a>'
40282 var template = tpl || this.tabTpl || false;
40285 template = new Roo.Template(
40286 Roo.bootstrap.version == 4 ?
40288 '<a class="nav-link" href="#" unselectable="on"' +
40289 (this.disableTooltips ? '' : ' title="{text}"') +
40292 '<a class="nav-link" href="#">' +
40293 '<span unselectable="on"' +
40294 (this.disableTooltips ? '' : ' title="{text}"') +
40295 ' >{text}</span></a>'
40300 switch (typeof(template)) {
40304 template = new Roo.Template(template);
40310 var el = template.overwrite(td, {"text": text});
40312 var inner = el.getElementsByTagName("span")[0];
40314 return {"el": el, "inner": inner};
40322 * @class Roo.TabPanelItem
40323 * @extends Roo.util.Observable
40324 * Represents an individual item (tab plus body) in a TabPanel.
40325 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40326 * @param {String} id The id of this TabPanelItem
40327 * @param {String} text The text for the tab of this TabPanelItem
40328 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40330 Roo.bootstrap.panel.TabItem = function(config){
40332 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40333 * @type Roo.TabPanel
40335 this.tabPanel = config.panel;
40337 * The id for this TabPanelItem
40340 this.id = config.id;
40342 this.disabled = false;
40344 this.text = config.text;
40346 this.loaded = false;
40347 this.closable = config.closable;
40350 * The body element for this TabPanelItem.
40351 * @type Roo.Element
40353 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40354 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40355 this.bodyEl.setStyle("display", "block");
40356 this.bodyEl.setStyle("zoom", "1");
40357 //this.hideAction();
40359 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40361 this.el = Roo.get(els.el);
40362 this.inner = Roo.get(els.inner, true);
40363 this.textEl = Roo.bootstrap.version == 4 ?
40364 this.el : Roo.get(this.el.dom.firstChild, true);
40366 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40367 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40370 // this.el.on("mousedown", this.onTabMouseDown, this);
40371 this.el.on("click", this.onTabClick, this);
40373 if(config.closable){
40374 var c = Roo.get(els.close, true);
40375 c.dom.title = this.closeText;
40376 c.addClassOnOver("close-over");
40377 c.on("click", this.closeClick, this);
40383 * Fires when this tab becomes the active tab.
40384 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40385 * @param {Roo.TabPanelItem} this
40389 * @event beforeclose
40390 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40391 * @param {Roo.TabPanelItem} this
40392 * @param {Object} e Set cancel to true on this object to cancel the close.
40394 "beforeclose": true,
40397 * Fires when this tab is closed.
40398 * @param {Roo.TabPanelItem} this
40402 * @event deactivate
40403 * Fires when this tab is no longer the active tab.
40404 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40405 * @param {Roo.TabPanelItem} this
40407 "deactivate" : true
40409 this.hidden = false;
40411 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40414 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40416 purgeListeners : function(){
40417 Roo.util.Observable.prototype.purgeListeners.call(this);
40418 this.el.removeAllListeners();
40421 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40424 this.status_node.addClass("active");
40427 this.tabPanel.stripWrap.repaint();
40429 this.fireEvent("activate", this.tabPanel, this);
40433 * Returns true if this tab is the active tab.
40434 * @return {Boolean}
40436 isActive : function(){
40437 return this.tabPanel.getActiveTab() == this;
40441 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40444 this.status_node.removeClass("active");
40446 this.fireEvent("deactivate", this.tabPanel, this);
40449 hideAction : function(){
40450 this.bodyEl.hide();
40451 this.bodyEl.setStyle("position", "absolute");
40452 this.bodyEl.setLeft("-20000px");
40453 this.bodyEl.setTop("-20000px");
40456 showAction : function(){
40457 this.bodyEl.setStyle("position", "relative");
40458 this.bodyEl.setTop("");
40459 this.bodyEl.setLeft("");
40460 this.bodyEl.show();
40464 * Set the tooltip for the tab.
40465 * @param {String} tooltip The tab's tooltip
40467 setTooltip : function(text){
40468 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40469 this.textEl.dom.qtip = text;
40470 this.textEl.dom.removeAttribute('title');
40472 this.textEl.dom.title = text;
40476 onTabClick : function(e){
40477 e.preventDefault();
40478 this.tabPanel.activate(this.id);
40481 onTabMouseDown : function(e){
40482 e.preventDefault();
40483 this.tabPanel.activate(this.id);
40486 getWidth : function(){
40487 return this.inner.getWidth();
40490 setWidth : function(width){
40491 var iwidth = width - this.linode.getPadding("lr");
40492 this.inner.setWidth(iwidth);
40493 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40494 this.linode.setWidth(width);
40498 * Show or hide the tab
40499 * @param {Boolean} hidden True to hide or false to show.
40501 setHidden : function(hidden){
40502 this.hidden = hidden;
40503 this.linode.setStyle("display", hidden ? "none" : "");
40507 * Returns true if this tab is "hidden"
40508 * @return {Boolean}
40510 isHidden : function(){
40511 return this.hidden;
40515 * Returns the text for this tab
40518 getText : function(){
40522 autoSize : function(){
40523 //this.el.beginMeasure();
40524 this.textEl.setWidth(1);
40526 * #2804 [new] Tabs in Roojs
40527 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40529 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40530 //this.el.endMeasure();
40534 * Sets the text for the tab (Note: this also sets the tooltip text)
40535 * @param {String} text The tab's text and tooltip
40537 setText : function(text){
40539 this.textEl.update(text);
40540 this.setTooltip(text);
40541 //if(!this.tabPanel.resizeTabs){
40542 // this.autoSize();
40546 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40548 activate : function(){
40549 this.tabPanel.activate(this.id);
40553 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40555 disable : function(){
40556 if(this.tabPanel.active != this){
40557 this.disabled = true;
40558 this.status_node.addClass("disabled");
40563 * Enables this TabPanelItem if it was previously disabled.
40565 enable : function(){
40566 this.disabled = false;
40567 this.status_node.removeClass("disabled");
40571 * Sets the content for this TabPanelItem.
40572 * @param {String} content The content
40573 * @param {Boolean} loadScripts true to look for and load scripts
40575 setContent : function(content, loadScripts){
40576 this.bodyEl.update(content, loadScripts);
40580 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40581 * @return {Roo.UpdateManager} The UpdateManager
40583 getUpdateManager : function(){
40584 return this.bodyEl.getUpdateManager();
40588 * Set a URL to be used to load the content for this TabPanelItem.
40589 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40590 * @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)
40591 * @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)
40592 * @return {Roo.UpdateManager} The UpdateManager
40594 setUrl : function(url, params, loadOnce){
40595 if(this.refreshDelegate){
40596 this.un('activate', this.refreshDelegate);
40598 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40599 this.on("activate", this.refreshDelegate);
40600 return this.bodyEl.getUpdateManager();
40604 _handleRefresh : function(url, params, loadOnce){
40605 if(!loadOnce || !this.loaded){
40606 var updater = this.bodyEl.getUpdateManager();
40607 updater.update(url, params, this._setLoaded.createDelegate(this));
40612 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40613 * Will fail silently if the setUrl method has not been called.
40614 * This does not activate the panel, just updates its content.
40616 refresh : function(){
40617 if(this.refreshDelegate){
40618 this.loaded = false;
40619 this.refreshDelegate();
40624 _setLoaded : function(){
40625 this.loaded = true;
40629 closeClick : function(e){
40632 this.fireEvent("beforeclose", this, o);
40633 if(o.cancel !== true){
40634 this.tabPanel.removeTab(this.id);
40638 * The text displayed in the tooltip for the close icon.
40641 closeText : "Close this tab"
40644 * This script refer to:
40645 * Title: International Telephone Input
40646 * Author: Jack O'Connor
40647 * Code version: v12.1.12
40648 * Availability: https://github.com/jackocnr/intl-tel-input.git
40651 Roo.bootstrap.PhoneInputData = function() {
40654 "Afghanistan (افغانستان)",
40659 "Albania (Shqipëri)",
40664 "Algeria (الجزائر)",
40689 "Antigua and Barbuda",
40699 "Armenia (Հայաստան)",
40715 "Austria (Österreich)",
40720 "Azerbaijan (Azərbaycan)",
40730 "Bahrain (البحرين)",
40735 "Bangladesh (বাংলাদেশ)",
40745 "Belarus (Беларусь)",
40750 "Belgium (België)",
40780 "Bosnia and Herzegovina (Босна и Херцеговина)",
40795 "British Indian Ocean Territory",
40800 "British Virgin Islands",
40810 "Bulgaria (България)",
40820 "Burundi (Uburundi)",
40825 "Cambodia (កម្ពុជា)",
40830 "Cameroon (Cameroun)",
40839 ["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"]
40842 "Cape Verde (Kabu Verdi)",
40847 "Caribbean Netherlands",
40858 "Central African Republic (République centrafricaine)",
40878 "Christmas Island",
40884 "Cocos (Keeling) Islands",
40895 "Comoros (جزر القمر)",
40900 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40905 "Congo (Republic) (Congo-Brazzaville)",
40925 "Croatia (Hrvatska)",
40946 "Czech Republic (Česká republika)",
40951 "Denmark (Danmark)",
40966 "Dominican Republic (República Dominicana)",
40970 ["809", "829", "849"]
40988 "Equatorial Guinea (Guinea Ecuatorial)",
41008 "Falkland Islands (Islas Malvinas)",
41013 "Faroe Islands (Føroyar)",
41034 "French Guiana (Guyane française)",
41039 "French Polynesia (Polynésie française)",
41054 "Georgia (საქართველო)",
41059 "Germany (Deutschland)",
41079 "Greenland (Kalaallit Nunaat)",
41116 "Guinea-Bissau (Guiné Bissau)",
41141 "Hungary (Magyarország)",
41146 "Iceland (Ísland)",
41166 "Iraq (العراق)",
41182 "Israel (ישראל)",
41209 "Jordan (الأردن)",
41214 "Kazakhstan (Казахстан)",
41235 "Kuwait (الكويت)",
41240 "Kyrgyzstan (Кыргызстан)",
41250 "Latvia (Latvija)",
41255 "Lebanon (لبنان)",
41270 "Libya (ليبيا)",
41280 "Lithuania (Lietuva)",
41295 "Macedonia (FYROM) (Македонија)",
41300 "Madagascar (Madagasikara)",
41330 "Marshall Islands",
41340 "Mauritania (موريتانيا)",
41345 "Mauritius (Moris)",
41366 "Moldova (Republica Moldova)",
41376 "Mongolia (Монгол)",
41381 "Montenegro (Crna Gora)",
41391 "Morocco (المغرب)",
41397 "Mozambique (Moçambique)",
41402 "Myanmar (Burma) (မြန်မာ)",
41407 "Namibia (Namibië)",
41422 "Netherlands (Nederland)",
41427 "New Caledonia (Nouvelle-Calédonie)",
41462 "North Korea (조선 민주주의 인민 공화국)",
41467 "Northern Mariana Islands",
41483 "Pakistan (پاکستان)",
41493 "Palestine (فلسطين)",
41503 "Papua New Guinea",
41545 "Réunion (La Réunion)",
41551 "Romania (România)",
41567 "Saint Barthélemy",
41578 "Saint Kitts and Nevis",
41588 "Saint Martin (Saint-Martin (partie française))",
41594 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41599 "Saint Vincent and the Grenadines",
41614 "São Tomé and Príncipe (São Tomé e Príncipe)",
41619 "Saudi Arabia (المملكة العربية السعودية)",
41624 "Senegal (Sénégal)",
41654 "Slovakia (Slovensko)",
41659 "Slovenia (Slovenija)",
41669 "Somalia (Soomaaliya)",
41679 "South Korea (대한민국)",
41684 "South Sudan (جنوب السودان)",
41694 "Sri Lanka (ශ්රී ලංකාව)",
41699 "Sudan (السودان)",
41709 "Svalbard and Jan Mayen",
41720 "Sweden (Sverige)",
41725 "Switzerland (Schweiz)",
41730 "Syria (سوريا)",
41775 "Trinidad and Tobago",
41780 "Tunisia (تونس)",
41785 "Turkey (Türkiye)",
41795 "Turks and Caicos Islands",
41805 "U.S. Virgin Islands",
41815 "Ukraine (Україна)",
41820 "United Arab Emirates (الإمارات العربية المتحدة)",
41842 "Uzbekistan (Oʻzbekiston)",
41852 "Vatican City (Città del Vaticano)",
41863 "Vietnam (Việt Nam)",
41868 "Wallis and Futuna (Wallis-et-Futuna)",
41873 "Western Sahara (الصحراء الغربية)",
41879 "Yemen (اليمن)",
41903 * This script refer to:
41904 * Title: International Telephone Input
41905 * Author: Jack O'Connor
41906 * Code version: v12.1.12
41907 * Availability: https://github.com/jackocnr/intl-tel-input.git
41911 * @class Roo.bootstrap.PhoneInput
41912 * @extends Roo.bootstrap.TriggerField
41913 * An input with International dial-code selection
41915 * @cfg {String} defaultDialCode default '+852'
41916 * @cfg {Array} preferedCountries default []
41919 * Create a new PhoneInput.
41920 * @param {Object} config Configuration options
41923 Roo.bootstrap.PhoneInput = function(config) {
41924 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41927 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41929 listWidth: undefined,
41931 selectedClass: 'active',
41933 invalidClass : "has-warning",
41935 validClass: 'has-success',
41937 allowed: '0123456789',
41942 * @cfg {String} defaultDialCode The default dial code when initializing the input
41944 defaultDialCode: '+852',
41947 * @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
41949 preferedCountries: false,
41951 getAutoCreate : function()
41953 var data = Roo.bootstrap.PhoneInputData();
41954 var align = this.labelAlign || this.parentLabelAlign();
41957 this.allCountries = [];
41958 this.dialCodeMapping = [];
41960 for (var i = 0; i < data.length; i++) {
41962 this.allCountries[i] = {
41966 priority: c[3] || 0,
41967 areaCodes: c[4] || null
41969 this.dialCodeMapping[c[2]] = {
41972 priority: c[3] || 0,
41973 areaCodes: c[4] || null
41985 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41986 maxlength: this.max_length,
41987 cls : 'form-control tel-input',
41988 autocomplete: 'new-password'
41991 var hiddenInput = {
41994 cls: 'hidden-tel-input'
41998 hiddenInput.name = this.name;
42001 if (this.disabled) {
42002 input.disabled = true;
42005 var flag_container = {
42022 cls: this.hasFeedback ? 'has-feedback' : '',
42028 cls: 'dial-code-holder',
42035 cls: 'roo-select2-container input-group',
42042 if (this.fieldLabel.length) {
42045 tooltip: 'This field is required'
42051 cls: 'control-label',
42057 html: this.fieldLabel
42060 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42066 if(this.indicatorpos == 'right') {
42067 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42074 if(align == 'left') {
42082 if(this.labelWidth > 12){
42083 label.style = "width: " + this.labelWidth + 'px';
42085 if(this.labelWidth < 13 && this.labelmd == 0){
42086 this.labelmd = this.labelWidth;
42088 if(this.labellg > 0){
42089 label.cls += ' col-lg-' + this.labellg;
42090 input.cls += ' col-lg-' + (12 - this.labellg);
42092 if(this.labelmd > 0){
42093 label.cls += ' col-md-' + this.labelmd;
42094 container.cls += ' col-md-' + (12 - this.labelmd);
42096 if(this.labelsm > 0){
42097 label.cls += ' col-sm-' + this.labelsm;
42098 container.cls += ' col-sm-' + (12 - this.labelsm);
42100 if(this.labelxs > 0){
42101 label.cls += ' col-xs-' + this.labelxs;
42102 container.cls += ' col-xs-' + (12 - this.labelxs);
42112 var settings = this;
42114 ['xs','sm','md','lg'].map(function(size){
42115 if (settings[size]) {
42116 cfg.cls += ' col-' + size + '-' + settings[size];
42120 this.store = new Roo.data.Store({
42121 proxy : new Roo.data.MemoryProxy({}),
42122 reader : new Roo.data.JsonReader({
42133 'name' : 'dialCode',
42137 'name' : 'priority',
42141 'name' : 'areaCodes',
42148 if(!this.preferedCountries) {
42149 this.preferedCountries = [
42156 var p = this.preferedCountries.reverse();
42159 for (var i = 0; i < p.length; i++) {
42160 for (var j = 0; j < this.allCountries.length; j++) {
42161 if(this.allCountries[j].iso2 == p[i]) {
42162 var t = this.allCountries[j];
42163 this.allCountries.splice(j,1);
42164 this.allCountries.unshift(t);
42170 this.store.proxy.data = {
42172 data: this.allCountries
42178 initEvents : function()
42181 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42183 this.indicator = this.indicatorEl();
42184 this.flag = this.flagEl();
42185 this.dialCodeHolder = this.dialCodeHolderEl();
42187 this.trigger = this.el.select('div.flag-box',true).first();
42188 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42193 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42194 _this.list.setWidth(lw);
42197 this.list.on('mouseover', this.onViewOver, this);
42198 this.list.on('mousemove', this.onViewMove, this);
42199 this.inputEl().on("keyup", this.onKeyUp, this);
42200 this.inputEl().on("keypress", this.onKeyPress, this);
42202 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42204 this.view = new Roo.View(this.list, this.tpl, {
42205 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42208 this.view.on('click', this.onViewClick, this);
42209 this.setValue(this.defaultDialCode);
42212 onTriggerClick : function(e)
42214 Roo.log('trigger click');
42219 if(this.isExpanded()){
42221 this.hasFocus = false;
42223 this.store.load({});
42224 this.hasFocus = true;
42229 isExpanded : function()
42231 return this.list.isVisible();
42234 collapse : function()
42236 if(!this.isExpanded()){
42240 Roo.get(document).un('mousedown', this.collapseIf, this);
42241 Roo.get(document).un('mousewheel', this.collapseIf, this);
42242 this.fireEvent('collapse', this);
42246 expand : function()
42250 if(this.isExpanded() || !this.hasFocus){
42254 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42255 this.list.setWidth(lw);
42258 this.restrictHeight();
42260 Roo.get(document).on('mousedown', this.collapseIf, this);
42261 Roo.get(document).on('mousewheel', this.collapseIf, this);
42263 this.fireEvent('expand', this);
42266 restrictHeight : function()
42268 this.list.alignTo(this.inputEl(), this.listAlign);
42269 this.list.alignTo(this.inputEl(), this.listAlign);
42272 onViewOver : function(e, t)
42274 if(this.inKeyMode){
42277 var item = this.view.findItemFromChild(t);
42280 var index = this.view.indexOf(item);
42281 this.select(index, false);
42286 onViewClick : function(view, doFocus, el, e)
42288 var index = this.view.getSelectedIndexes()[0];
42290 var r = this.store.getAt(index);
42293 this.onSelect(r, index);
42295 if(doFocus !== false && !this.blockFocus){
42296 this.inputEl().focus();
42300 onViewMove : function(e, t)
42302 this.inKeyMode = false;
42305 select : function(index, scrollIntoView)
42307 this.selectedIndex = index;
42308 this.view.select(index);
42309 if(scrollIntoView !== false){
42310 var el = this.view.getNode(index);
42312 this.list.scrollChildIntoView(el, false);
42317 createList : function()
42319 this.list = Roo.get(document.body).createChild({
42321 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42322 style: 'display:none'
42325 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42328 collapseIf : function(e)
42330 var in_combo = e.within(this.el);
42331 var in_list = e.within(this.list);
42332 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42334 if (in_combo || in_list || is_list) {
42340 onSelect : function(record, index)
42342 if(this.fireEvent('beforeselect', this, record, index) !== false){
42344 this.setFlagClass(record.data.iso2);
42345 this.setDialCode(record.data.dialCode);
42346 this.hasFocus = false;
42348 this.fireEvent('select', this, record, index);
42352 flagEl : function()
42354 var flag = this.el.select('div.flag',true).first();
42361 dialCodeHolderEl : function()
42363 var d = this.el.select('input.dial-code-holder',true).first();
42370 setDialCode : function(v)
42372 this.dialCodeHolder.dom.value = '+'+v;
42375 setFlagClass : function(n)
42377 this.flag.dom.className = 'flag '+n;
42380 getValue : function()
42382 var v = this.inputEl().getValue();
42383 if(this.dialCodeHolder) {
42384 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42389 setValue : function(v)
42391 var d = this.getDialCode(v);
42393 //invalid dial code
42394 if(v.length == 0 || !d || d.length == 0) {
42396 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42397 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42403 this.setFlagClass(this.dialCodeMapping[d].iso2);
42404 this.setDialCode(d);
42405 this.inputEl().dom.value = v.replace('+'+d,'');
42406 this.hiddenEl().dom.value = this.getValue();
42411 getDialCode : function(v)
42415 if (v.length == 0) {
42416 return this.dialCodeHolder.dom.value;
42420 if (v.charAt(0) != "+") {
42423 var numericChars = "";
42424 for (var i = 1; i < v.length; i++) {
42425 var c = v.charAt(i);
42428 if (this.dialCodeMapping[numericChars]) {
42429 dialCode = v.substr(1, i);
42431 if (numericChars.length == 4) {
42441 this.setValue(this.defaultDialCode);
42445 hiddenEl : function()
42447 return this.el.select('input.hidden-tel-input',true).first();
42450 // after setting val
42451 onKeyUp : function(e){
42452 this.setValue(this.getValue());
42455 onKeyPress : function(e){
42456 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42463 * @class Roo.bootstrap.MoneyField
42464 * @extends Roo.bootstrap.ComboBox
42465 * Bootstrap MoneyField class
42468 * Create a new MoneyField.
42469 * @param {Object} config Configuration options
42472 Roo.bootstrap.MoneyField = function(config) {
42474 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42478 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42481 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42483 allowDecimals : true,
42485 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42487 decimalSeparator : ".",
42489 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42491 decimalPrecision : 0,
42493 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42495 allowNegative : true,
42497 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42501 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42503 minValue : Number.NEGATIVE_INFINITY,
42505 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42507 maxValue : Number.MAX_VALUE,
42509 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42511 minText : "The minimum value for this field is {0}",
42513 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42515 maxText : "The maximum value for this field is {0}",
42517 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42518 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42520 nanText : "{0} is not a valid number",
42522 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42526 * @cfg {String} defaults currency of the MoneyField
42527 * value should be in lkey
42529 defaultCurrency : false,
42531 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42533 thousandsDelimiter : false,
42535 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42546 getAutoCreate : function()
42548 var align = this.labelAlign || this.parentLabelAlign();
42560 cls : 'form-control roo-money-amount-input',
42561 autocomplete: 'new-password'
42564 var hiddenInput = {
42568 cls: 'hidden-number-input'
42571 if(this.max_length) {
42572 input.maxlength = this.max_length;
42576 hiddenInput.name = this.name;
42579 if (this.disabled) {
42580 input.disabled = true;
42583 var clg = 12 - this.inputlg;
42584 var cmd = 12 - this.inputmd;
42585 var csm = 12 - this.inputsm;
42586 var cxs = 12 - this.inputxs;
42590 cls : 'row roo-money-field',
42594 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42598 cls: 'roo-select2-container input-group',
42602 cls : 'form-control roo-money-currency-input',
42603 autocomplete: 'new-password',
42605 name : this.currencyName
42609 cls : 'input-group-addon',
42623 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42627 cls: this.hasFeedback ? 'has-feedback' : '',
42638 if (this.fieldLabel.length) {
42641 tooltip: 'This field is required'
42647 cls: 'control-label',
42653 html: this.fieldLabel
42656 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42662 if(this.indicatorpos == 'right') {
42663 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42670 if(align == 'left') {
42678 if(this.labelWidth > 12){
42679 label.style = "width: " + this.labelWidth + 'px';
42681 if(this.labelWidth < 13 && this.labelmd == 0){
42682 this.labelmd = this.labelWidth;
42684 if(this.labellg > 0){
42685 label.cls += ' col-lg-' + this.labellg;
42686 input.cls += ' col-lg-' + (12 - this.labellg);
42688 if(this.labelmd > 0){
42689 label.cls += ' col-md-' + this.labelmd;
42690 container.cls += ' col-md-' + (12 - this.labelmd);
42692 if(this.labelsm > 0){
42693 label.cls += ' col-sm-' + this.labelsm;
42694 container.cls += ' col-sm-' + (12 - this.labelsm);
42696 if(this.labelxs > 0){
42697 label.cls += ' col-xs-' + this.labelxs;
42698 container.cls += ' col-xs-' + (12 - this.labelxs);
42709 var settings = this;
42711 ['xs','sm','md','lg'].map(function(size){
42712 if (settings[size]) {
42713 cfg.cls += ' col-' + size + '-' + settings[size];
42720 initEvents : function()
42722 this.indicator = this.indicatorEl();
42724 this.initCurrencyEvent();
42726 this.initNumberEvent();
42729 initCurrencyEvent : function()
42732 throw "can not find store for combo";
42735 this.store = Roo.factory(this.store, Roo.data);
42736 this.store.parent = this;
42740 this.triggerEl = this.el.select('.input-group-addon', true).first();
42742 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42747 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42748 _this.list.setWidth(lw);
42751 this.list.on('mouseover', this.onViewOver, this);
42752 this.list.on('mousemove', this.onViewMove, this);
42753 this.list.on('scroll', this.onViewScroll, this);
42756 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42759 this.view = new Roo.View(this.list, this.tpl, {
42760 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42763 this.view.on('click', this.onViewClick, this);
42765 this.store.on('beforeload', this.onBeforeLoad, this);
42766 this.store.on('load', this.onLoad, this);
42767 this.store.on('loadexception', this.onLoadException, this);
42769 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42770 "up" : function(e){
42771 this.inKeyMode = true;
42775 "down" : function(e){
42776 if(!this.isExpanded()){
42777 this.onTriggerClick();
42779 this.inKeyMode = true;
42784 "enter" : function(e){
42787 if(this.fireEvent("specialkey", this, e)){
42788 this.onViewClick(false);
42794 "esc" : function(e){
42798 "tab" : function(e){
42801 if(this.fireEvent("specialkey", this, e)){
42802 this.onViewClick(false);
42810 doRelay : function(foo, bar, hname){
42811 if(hname == 'down' || this.scope.isExpanded()){
42812 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42820 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42824 initNumberEvent : function(e)
42826 this.inputEl().on("keydown" , this.fireKey, this);
42827 this.inputEl().on("focus", this.onFocus, this);
42828 this.inputEl().on("blur", this.onBlur, this);
42830 this.inputEl().relayEvent('keyup', this);
42832 if(this.indicator){
42833 this.indicator.addClass('invisible');
42836 this.originalValue = this.getValue();
42838 if(this.validationEvent == 'keyup'){
42839 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42840 this.inputEl().on('keyup', this.filterValidation, this);
42842 else if(this.validationEvent !== false){
42843 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42846 if(this.selectOnFocus){
42847 this.on("focus", this.preFocus, this);
42850 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42851 this.inputEl().on("keypress", this.filterKeys, this);
42853 this.inputEl().relayEvent('keypress', this);
42856 var allowed = "0123456789";
42858 if(this.allowDecimals){
42859 allowed += this.decimalSeparator;
42862 if(this.allowNegative){
42866 if(this.thousandsDelimiter) {
42870 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42872 var keyPress = function(e){
42874 var k = e.getKey();
42876 var c = e.getCharCode();
42879 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42880 allowed.indexOf(String.fromCharCode(c)) === -1
42886 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42890 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42895 this.inputEl().on("keypress", keyPress, this);
42899 onTriggerClick : function(e)
42906 this.loadNext = false;
42908 if(this.isExpanded()){
42913 this.hasFocus = true;
42915 if(this.triggerAction == 'all') {
42916 this.doQuery(this.allQuery, true);
42920 this.doQuery(this.getRawValue());
42923 getCurrency : function()
42925 var v = this.currencyEl().getValue();
42930 restrictHeight : function()
42932 this.list.alignTo(this.currencyEl(), this.listAlign);
42933 this.list.alignTo(this.currencyEl(), this.listAlign);
42936 onViewClick : function(view, doFocus, el, e)
42938 var index = this.view.getSelectedIndexes()[0];
42940 var r = this.store.getAt(index);
42943 this.onSelect(r, index);
42947 onSelect : function(record, index){
42949 if(this.fireEvent('beforeselect', this, record, index) !== false){
42951 this.setFromCurrencyData(index > -1 ? record.data : false);
42955 this.fireEvent('select', this, record, index);
42959 setFromCurrencyData : function(o)
42963 this.lastCurrency = o;
42965 if (this.currencyField) {
42966 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42968 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
42971 this.lastSelectionText = currency;
42973 //setting default currency
42974 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42975 this.setCurrency(this.defaultCurrency);
42979 this.setCurrency(currency);
42982 setFromData : function(o)
42986 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42988 this.setFromCurrencyData(c);
42993 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42995 Roo.log('no value set for '+ (this.name ? this.name : this.id));
42998 this.setValue(value);
43002 setCurrency : function(v)
43004 this.currencyValue = v;
43007 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43012 setValue : function(v)
43014 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43020 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43022 this.inputEl().dom.value = (v == '') ? '' :
43023 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43025 if(!this.allowZero && v === '0') {
43026 this.hiddenEl().dom.value = '';
43027 this.inputEl().dom.value = '';
43034 getRawValue : function()
43036 var v = this.inputEl().getValue();
43041 getValue : function()
43043 return this.fixPrecision(this.parseValue(this.getRawValue()));
43046 parseValue : function(value)
43048 if(this.thousandsDelimiter) {
43050 r = new RegExp(",", "g");
43051 value = value.replace(r, "");
43054 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43055 return isNaN(value) ? '' : value;
43059 fixPrecision : function(value)
43061 if(this.thousandsDelimiter) {
43063 r = new RegExp(",", "g");
43064 value = value.replace(r, "");
43067 var nan = isNaN(value);
43069 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43070 return nan ? '' : value;
43072 return parseFloat(value).toFixed(this.decimalPrecision);
43075 decimalPrecisionFcn : function(v)
43077 return Math.floor(v);
43080 validateValue : function(value)
43082 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43086 var num = this.parseValue(value);
43089 this.markInvalid(String.format(this.nanText, value));
43093 if(num < this.minValue){
43094 this.markInvalid(String.format(this.minText, this.minValue));
43098 if(num > this.maxValue){
43099 this.markInvalid(String.format(this.maxText, this.maxValue));
43106 validate : function()
43108 if(this.disabled || this.allowBlank){
43113 var currency = this.getCurrency();
43115 if(this.validateValue(this.getRawValue()) && currency.length){
43120 this.markInvalid();
43124 getName: function()
43129 beforeBlur : function()
43135 var v = this.parseValue(this.getRawValue());
43142 onBlur : function()
43146 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43147 //this.el.removeClass(this.focusClass);
43150 this.hasFocus = false;
43152 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43156 var v = this.getValue();
43158 if(String(v) !== String(this.startValue)){
43159 this.fireEvent('change', this, v, this.startValue);
43162 this.fireEvent("blur", this);
43165 inputEl : function()
43167 return this.el.select('.roo-money-amount-input', true).first();
43170 currencyEl : function()
43172 return this.el.select('.roo-money-currency-input', true).first();
43175 hiddenEl : function()
43177 return this.el.select('input.hidden-number-input',true).first();
43181 * @class Roo.bootstrap.BezierSignature
43182 * @extends Roo.bootstrap.Component
43183 * Bootstrap BezierSignature class
43184 * This script refer to:
43185 * Title: Signature Pad
43187 * Availability: https://github.com/szimek/signature_pad
43190 * Create a new BezierSignature
43191 * @param {Object} config The config object
43194 Roo.bootstrap.BezierSignature = function(config){
43195 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43201 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43208 mouse_btn_down: true,
43211 * @cfg {int} canvas height
43213 canvas_height: '200px',
43216 * @cfg {float|function} Radius of a single dot.
43221 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43226 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43231 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43236 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43241 * @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.
43243 bg_color: 'rgba(0, 0, 0, 0)',
43246 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43248 dot_color: 'black',
43251 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43253 velocity_filter_weight: 0.7,
43256 * @cfg {function} Callback when stroke begin.
43261 * @cfg {function} Callback when stroke end.
43265 getAutoCreate : function()
43267 var cls = 'roo-signature column';
43270 cls += ' ' + this.cls;
43280 for(var i = 0; i < col_sizes.length; i++) {
43281 if(this[col_sizes[i]]) {
43282 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43292 cls: 'roo-signature-body',
43296 cls: 'roo-signature-body-canvas',
43297 height: this.canvas_height,
43298 width: this.canvas_width
43305 style: 'display: none'
43313 initEvents: function()
43315 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43317 var canvas = this.canvasEl();
43319 // mouse && touch event swapping...
43320 canvas.dom.style.touchAction = 'none';
43321 canvas.dom.style.msTouchAction = 'none';
43323 this.mouse_btn_down = false;
43324 canvas.on('mousedown', this._handleMouseDown, this);
43325 canvas.on('mousemove', this._handleMouseMove, this);
43326 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43328 if (window.PointerEvent) {
43329 canvas.on('pointerdown', this._handleMouseDown, this);
43330 canvas.on('pointermove', this._handleMouseMove, this);
43331 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43334 if ('ontouchstart' in window) {
43335 canvas.on('touchstart', this._handleTouchStart, this);
43336 canvas.on('touchmove', this._handleTouchMove, this);
43337 canvas.on('touchend', this._handleTouchEnd, this);
43340 Roo.EventManager.onWindowResize(this.resize, this, true);
43342 // file input event
43343 this.fileEl().on('change', this.uploadImage, this);
43350 resize: function(){
43352 var canvas = this.canvasEl().dom;
43353 var ctx = this.canvasElCtx();
43354 var img_data = false;
43356 if(canvas.width > 0) {
43357 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43359 // setting canvas width will clean img data
43362 var style = window.getComputedStyle ?
43363 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43365 var padding_left = parseInt(style.paddingLeft) || 0;
43366 var padding_right = parseInt(style.paddingRight) || 0;
43368 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43371 ctx.putImageData(img_data, 0, 0);
43375 _handleMouseDown: function(e)
43377 if (e.browserEvent.which === 1) {
43378 this.mouse_btn_down = true;
43379 this.strokeBegin(e);
43383 _handleMouseMove: function (e)
43385 if (this.mouse_btn_down) {
43386 this.strokeMoveUpdate(e);
43390 _handleMouseUp: function (e)
43392 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43393 this.mouse_btn_down = false;
43398 _handleTouchStart: function (e) {
43400 e.preventDefault();
43401 if (e.browserEvent.targetTouches.length === 1) {
43402 // var touch = e.browserEvent.changedTouches[0];
43403 // this.strokeBegin(touch);
43405 this.strokeBegin(e); // assume e catching the correct xy...
43409 _handleTouchMove: function (e) {
43410 e.preventDefault();
43411 // var touch = event.targetTouches[0];
43412 // _this._strokeMoveUpdate(touch);
43413 this.strokeMoveUpdate(e);
43416 _handleTouchEnd: function (e) {
43417 var wasCanvasTouched = e.target === this.canvasEl().dom;
43418 if (wasCanvasTouched) {
43419 e.preventDefault();
43420 // var touch = event.changedTouches[0];
43421 // _this._strokeEnd(touch);
43426 reset: function () {
43427 this._lastPoints = [];
43428 this._lastVelocity = 0;
43429 this._lastWidth = (this.min_width + this.max_width) / 2;
43430 this.canvasElCtx().fillStyle = this.dot_color;
43433 strokeMoveUpdate: function(e)
43435 this.strokeUpdate(e);
43437 if (this.throttle) {
43438 this.throttleStroke(this.strokeUpdate, this.throttle);
43441 this.strokeUpdate(e);
43445 strokeBegin: function(e)
43447 var newPointGroup = {
43448 color: this.dot_color,
43452 if (typeof this.onBegin === 'function') {
43456 this.curve_data.push(newPointGroup);
43458 this.strokeUpdate(e);
43461 strokeUpdate: function(e)
43463 var rect = this.canvasEl().dom.getBoundingClientRect();
43464 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43465 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43466 var lastPoints = lastPointGroup.points;
43467 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43468 var isLastPointTooClose = lastPoint
43469 ? point.distanceTo(lastPoint) <= this.min_distance
43471 var color = lastPointGroup.color;
43472 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43473 var curve = this.addPoint(point);
43475 this.drawDot({color: color, point: point});
43478 this.drawCurve({color: color, curve: curve});
43488 strokeEnd: function(e)
43490 this.strokeUpdate(e);
43491 if (typeof this.onEnd === 'function') {
43496 addPoint: function (point) {
43497 var _lastPoints = this._lastPoints;
43498 _lastPoints.push(point);
43499 if (_lastPoints.length > 2) {
43500 if (_lastPoints.length === 3) {
43501 _lastPoints.unshift(_lastPoints[0]);
43503 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43504 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43505 _lastPoints.shift();
43511 calculateCurveWidths: function (startPoint, endPoint) {
43512 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43513 (1 - this.velocity_filter_weight) * this._lastVelocity;
43515 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43518 start: this._lastWidth
43521 this._lastVelocity = velocity;
43522 this._lastWidth = newWidth;
43526 drawDot: function (_a) {
43527 var color = _a.color, point = _a.point;
43528 var ctx = this.canvasElCtx();
43529 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43531 this.drawCurveSegment(point.x, point.y, width);
43533 ctx.fillStyle = color;
43537 drawCurve: function (_a) {
43538 var color = _a.color, curve = _a.curve;
43539 var ctx = this.canvasElCtx();
43540 var widthDelta = curve.endWidth - curve.startWidth;
43541 var drawSteps = Math.floor(curve.length()) * 2;
43543 ctx.fillStyle = color;
43544 for (var i = 0; i < drawSteps; i += 1) {
43545 var t = i / drawSteps;
43551 var x = uuu * curve.startPoint.x;
43552 x += 3 * uu * t * curve.control1.x;
43553 x += 3 * u * tt * curve.control2.x;
43554 x += ttt * curve.endPoint.x;
43555 var y = uuu * curve.startPoint.y;
43556 y += 3 * uu * t * curve.control1.y;
43557 y += 3 * u * tt * curve.control2.y;
43558 y += ttt * curve.endPoint.y;
43559 var width = curve.startWidth + ttt * widthDelta;
43560 this.drawCurveSegment(x, y, width);
43566 drawCurveSegment: function (x, y, width) {
43567 var ctx = this.canvasElCtx();
43569 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43570 this.is_empty = false;
43575 var ctx = this.canvasElCtx();
43576 var canvas = this.canvasEl().dom;
43577 ctx.fillStyle = this.bg_color;
43578 ctx.clearRect(0, 0, canvas.width, canvas.height);
43579 ctx.fillRect(0, 0, canvas.width, canvas.height);
43580 this.curve_data = [];
43582 this.is_empty = true;
43587 return this.el.select('input',true).first();
43590 canvasEl: function()
43592 return this.el.select('canvas',true).first();
43595 canvasElCtx: function()
43597 return this.el.select('canvas',true).first().dom.getContext('2d');
43600 getImage: function(type)
43602 if(this.is_empty) {
43607 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43610 drawFromImage: function(img_src)
43612 var img = new Image();
43614 img.onload = function(){
43615 this.canvasElCtx().drawImage(img, 0, 0);
43620 this.is_empty = false;
43623 selectImage: function()
43625 this.fileEl().dom.click();
43628 uploadImage: function(e)
43630 var reader = new FileReader();
43632 reader.onload = function(e){
43633 var img = new Image();
43634 img.onload = function(){
43636 this.canvasElCtx().drawImage(img, 0, 0);
43638 img.src = e.target.result;
43641 reader.readAsDataURL(e.target.files[0]);
43644 // Bezier Point Constructor
43645 Point: (function () {
43646 function Point(x, y, time) {
43649 this.time = time || Date.now();
43651 Point.prototype.distanceTo = function (start) {
43652 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43654 Point.prototype.equals = function (other) {
43655 return this.x === other.x && this.y === other.y && this.time === other.time;
43657 Point.prototype.velocityFrom = function (start) {
43658 return this.time !== start.time
43659 ? this.distanceTo(start) / (this.time - start.time)
43666 // Bezier Constructor
43667 Bezier: (function () {
43668 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43669 this.startPoint = startPoint;
43670 this.control2 = control2;
43671 this.control1 = control1;
43672 this.endPoint = endPoint;
43673 this.startWidth = startWidth;
43674 this.endWidth = endWidth;
43676 Bezier.fromPoints = function (points, widths, scope) {
43677 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43678 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43679 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43681 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43682 var dx1 = s1.x - s2.x;
43683 var dy1 = s1.y - s2.y;
43684 var dx2 = s2.x - s3.x;
43685 var dy2 = s2.y - s3.y;
43686 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43687 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43688 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43689 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43690 var dxm = m1.x - m2.x;
43691 var dym = m1.y - m2.y;
43692 var k = l2 / (l1 + l2);
43693 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43694 var tx = s2.x - cm.x;
43695 var ty = s2.y - cm.y;
43697 c1: new scope.Point(m1.x + tx, m1.y + ty),
43698 c2: new scope.Point(m2.x + tx, m2.y + ty)
43701 Bezier.prototype.length = function () {
43706 for (var i = 0; i <= steps; i += 1) {
43708 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43709 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43711 var xdiff = cx - px;
43712 var ydiff = cy - py;
43713 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43720 Bezier.prototype.point = function (t, start, c1, c2, end) {
43721 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43722 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43723 + (3.0 * c2 * (1.0 - t) * t * t)
43724 + (end * t * t * t);
43729 throttleStroke: function(fn, wait) {
43730 if (wait === void 0) { wait = 250; }
43732 var timeout = null;
43736 var later = function () {
43737 previous = Date.now();
43739 result = fn.apply(storedContext, storedArgs);
43741 storedContext = null;
43745 return function wrapper() {
43747 for (var _i = 0; _i < arguments.length; _i++) {
43748 args[_i] = arguments[_i];
43750 var now = Date.now();
43751 var remaining = wait - (now - previous);
43752 storedContext = this;
43754 if (remaining <= 0 || remaining > wait) {
43756 clearTimeout(timeout);
43760 result = fn.apply(storedContext, storedArgs);
43762 storedContext = null;
43766 else if (!timeout) {
43767 timeout = window.setTimeout(later, remaining);